<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Models\Company;
use App\Models\Credit;
use Illuminate\Database\UniqueConstraintViolationException;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

/**
 * PWA-002: crédito, aporte y retiro guardan la clave de idempotencia del teléfono,
 * única por empresa. Solo se mira el esquema ya migrado: la base de los tests es
 * la de desarrollo y un DDL hace commit implícito.
 */
class IdempotencyKeyColumnsTest extends TestCase
{
    use DatabaseTransactions;

    private const INDICES = [
        'credits' => 'credits_company_idempotency_unique',
        'capital_contributions' => 'capital_contributions_company_idempotency_unique',
        'partner_withdrawals' => 'partner_withdrawals_company_idempotency_unique',
    ];

    #[Test]
    public function las_tres_tablas_tienen_la_columna_y_su_indice_unico_por_empresa(): void
    {
        foreach (self::INDICES as $tabla => $nombre) {
            $columna = collect(Schema::getColumns($tabla))->firstWhere('name', 'idempotency_key');
            $this->assertNotNull($columna, "{$tabla} sin idempotency_key");
            $this->assertTrue($columna['nullable'], "{$tabla}.idempotency_key debe ser nullable");

            $indice = collect(Schema::getIndexes($tabla))->firstWhere('name', $nombre);
            $this->assertNotNull($indice, "{$tabla} sin el índice {$nombre}");
            $this->assertTrue($indice['unique'], "{$nombre} no es único");
            $this->assertSame(['company_id', 'idempotency_key'], $indice['columns']);
        }
    }

    #[Test]
    public function el_indice_de_creditos_rechaza_la_misma_clave_en_la_empresa(): void
    {
        $empresa = Company::factory()->create();
        $clave = (string) Str::uuid();
        $primero = Credit::factory()->create(['company_id' => $empresa->id, 'idempotency_key' => $clave]);

        $this->expectException(UniqueConstraintViolationException::class);
        $this->expectExceptionMessage('credits_company_idempotency_unique');

        // Mismos padres que el primero: así solo puede chocar el índice de la clave.
        Credit::factory()->create([
            'company_id' => $empresa->id,
            'client_id' => $primero->client_id,
            'collector_user_id' => $primero->collector_user_id,
            'created_by_user_id' => $primero->created_by_user_id,
            'idempotency_key' => $clave,
        ]);
    }

    #[Test]
    public function la_misma_clave_en_otra_empresa_no_choca(): void
    {
        $clave = (string) Str::uuid();
        Credit::factory()->create(['company_id' => Company::factory()->create()->id, 'idempotency_key' => $clave]);
        Credit::factory()->create(['company_id' => Company::factory()->create()->id, 'idempotency_key' => $clave]);

        $this->assertSame(2, Credit::withoutGlobalScopes()->where('idempotency_key', $clave)->count());
    }
}
