<?php

declare(strict_types=1);

namespace Tests\Feature\Dashboard;

use App\Models\Company;
use App\Models\Credit;
use App\Models\Installment;
use App\Models\Payment;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * La BD de dev es una copia de produccion, pero CI corre con BD limpia: cada
 * test crea su propia empresa y solo asevera sobre ella.
 */
class AdminPayloadTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $admin;

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

        Role::firstOrCreate(['name' => 'admin', 'guard_name' => 'web']);

        $this->company = Company::factory()->create(['status' => Company::STATUS_ACTIVE]);

        // EnsurePwaAccess exige suscripcion activa para llegar al endpoint.
        Subscription::factory()->create([
            'company_id' => $this->company->id,
            'plan_id' => Plan::factory()->create()->id,
            'status' => Subscription::STATUS_ACTIVE,
            'is_active' => true,
            'starts_at' => now()->subMonth(),
            'ends_at' => now()->addYear(),
        ]);

        $this->admin = User::factory()->create(['company_id' => $this->company->id]);
        $this->admin->assignRole('admin');
    }

    #[Test]
    public function el_dueno_recibe_la_meta_del_dia(): void
    {
        // Antes, `due_today` solo se armaba en la rama del cobrador, asi que la
        // barra de progreso del dueno estaba SIEMPRE en cero.
        $credit = Credit::factory()->create([
            'company_id' => $this->company->id,
            'status' => Credit::STATUS_ACTIVE,
        ]);

        Installment::factory()->create([
            'company_id' => $this->company->id,
            'credit_id' => $credit->id,
            'due_date' => now()->toDateString(),
            'total_amount' => 60000,
            'amount_paid' => 0,
            'status' => Installment::STATUS_PENDING,
        ]);

        $response = $this->actingAs($this->admin, 'sanctum')->getJson('/api/pwa/dashboard');

        $response->assertOk();
        $this->assertSame('admin', $response->json('role'));
        $this->assertEqualsWithDelta(60000.0, (float) $response->json('stats.due_today.amount'), 0.01);
        $this->assertSame(1, (int) $response->json('stats.due_today.count'));
    }

    #[Test]
    public function el_margen_sin_ingresos_es_no_data_nunca_cero(): void
    {
        // El contrato anti-ceros: un margen que no se puede calcular NO puede
        // renderizarse como 0% — tiene que decir que no hay datos.
        $response = $this->actingAs($this->admin, 'sanctum')->getJson('/api/pwa/dashboard');

        $response->assertOk();
        $this->assertSame('no_data', $response->json('stats.profitability.margin.state'));
        $this->assertNull($response->json('stats.profitability.margin.value'));
    }

    #[Test]
    public function una_utilidad_de_cero_legitima_si_es_ok(): void
    {
        // Cero legitimo (no hubo movimiento) != dato ausente.
        $response = $this->actingAs($this->admin, 'sanctum')->getJson('/api/pwa/dashboard');

        $response->assertOk();
        $this->assertSame('ok', $response->json('stats.profitability.net_profit.state'));
        $this->assertEqualsWithDelta(0.0, (float) $response->json('stats.profitability.net_profit.value'), 0.01);
    }

    #[Test]
    public function una_utilidad_sin_costos_cargados_se_marca_unreliable(): void
    {
        // Sin gastos, utilidad = interes bruto con otro nombre. Es EL bug que este
        // panel vino a corregir, asi que la titular tiene que advertirlo, no
        // presumir una utilidad sana.
        $credit = Credit::factory()->create([
            'company_id' => $this->company->id,
            'status' => Credit::STATUS_ACTIVE,
        ]);

        $installment = Installment::factory()->create([
            'company_id' => $this->company->id,
            'credit_id' => $credit->id,
            'interest_amount' => 10000,
            'principal_amount' => 50000,
            'total_amount' => 60000,
        ]);

        $payment = Payment::create([
            'company_id' => $this->company->id,
            'credit_id' => $credit->id,
            'amount' => 25000,
            'payment_date' => now()->toDateString(),
            'registered_by_user_id' => $this->admin->id,
            'voided' => false,
        ]);

        DB::table('payment_installment')->insert([
            'payment_id' => $payment->id,
            'installment_id' => $installment->id,
            'applied_amount' => 25000,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $response = $this->actingAs($this->admin, 'sanctum')->getJson('/api/pwa/dashboard');

        $response->assertOk();
        $this->assertSame('unreliable', $response->json('stats.profitability.net_profit.state'));
        $this->assertEqualsWithDelta(10000.0, (float) $response->json('stats.profitability.net_profit.value'), 0.01);
        $this->assertNotNull($response->json('stats.profitability.net_profit.note'));
    }

    #[Test]
    public function el_ritmo_trae_el_mes_en_curso_con_dias_transcurridos(): void
    {
        // "van N de M dias" evita que el mes en curso se lea como un desplome.
        $response = $this->actingAs($this->admin, 'sanctum')->getJson('/api/pwa/dashboard');

        $response->assertOk();
        $this->assertSame(
            (int) now()->day,
            (int) $response->json('stats.ritmo.month_to_date.days_elapsed')
        );
        $this->assertSame(
            (int) now()->daysInMonth,
            (int) $response->json('stats.ritmo.month_to_date.days_in_month')
        );
        $this->assertNotNull($response->json('stats.ritmo.last_month.amount'));
    }

    #[Test]
    public function el_sparkline_cubre_30_dias(): void
    {
        $response = $this->actingAs($this->admin, 'sanctum')->getJson('/api/pwa/dashboard');

        $response->assertOk();
        $this->assertCount(30, $response->json('stats.collections_trend'));
    }
}
