<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Exceptions\PlanLimitExceededException;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Installment;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use App\Services\CreditOperationService;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

/**
 * Auditoría R5 — el clon "editable" ("Duplicar") es un crédito NUEVO con desembolso
 * real que NO cierra al padre (suma +1 al conteo de créditos activos), así que debe
 * respetar el cupo del plan igual que crear un crédito normal. Antes se colaba sin
 * control y podía superar max_active_credits / max_monthly_volume.
 */
class CloneCreditPlanLimitTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $user;

    private function setupCompanyOnPlan(int $maxActiveCredits): void
    {
        $this->company = Company::factory()->create(['interest_method' => 'flat_rate']);
        $plan = Plan::factory()->create([
            'max_users' => 0, 'max_collectors' => 0, 'max_active_credits' => $maxActiveCredits,
            'max_clients' => 0, 'max_monthly_volume' => 0,
        ]);
        Subscription::factory()->active()->create([
            'company_id' => $this->company->id, 'plan_id' => $plan->id, 'ends_at' => now()->addYear(),
        ]);
        $this->user = User::factory()->create(['company_id' => $this->company->id]);
        $this->actingAs($this->user);
    }

    /** Crédito activo SIN pagos → clonable (sin capital pagado, saldo > 0). */
    private function activeCreditWithoutPayments(float $amount, float $rate, int $installments): Credit
    {
        $total = $amount * (1 + $rate / 100);
        $per = $total / $installments;
        $client = Client::factory()->create(['company_id' => $this->company->id]);

        $credit = Credit::create([
            'company_id' => $this->company->id,
            'client_id' => $client->id,
            'collector_user_id' => $this->user->id,
            'created_by_user_id' => $this->user->id,
            'status' => Credit::STATUS_ACTIVE,
            'amount' => $amount,
            'interest_rate' => $rate,
            'installments_count' => $installments,
            'periodicity' => 'weekly',
            'start_date' => Carbon::now()->subWeeks($installments),
        ]);

        for ($i = 1; $i <= $installments; $i++) {
            Installment::create([
                'company_id' => $this->company->id,
                'credit_id' => $credit->id,
                'installment_number' => $i,
                'status' => Installment::STATUS_PENDING,
                'due_date' => Carbon::now()->subWeeks($installments - $i),
                'principal_amount' => $amount / $installments,
                'interest_amount' => ($amount * $rate / 100) / $installments,
                'total_amount' => $per,
                'principal_balance_after' => $per,
                'amount_paid' => 0,
            ]);
        }

        return $credit->fresh(['installments']);
    }

    private function cloneCredit(Credit $credit): Credit
    {
        return app(CreditOperationService::class)->executeOperation($credit, [
            'operation_type' => 'clone',
            'new_installments' => $credit->installments_count,
            'amount' => $credit->amount,
        ]);
    }

    #[Test]
    public function cloning_over_the_active_credit_cap_is_blocked(): void
    {
        $this->setupCompanyOnPlan(maxActiveCredits: 1);
        $credit = $this->activeCreditWithoutPayments(500000, 20, 10); // 1/1 activos

        $blocked = false;
        try {
            $this->cloneCredit($credit);
        } catch (PlanLimitExceededException) {
            $blocked = true;
        }

        $this->assertTrue($blocked, 'el clon debe bloquearse por el cupo de créditos activos');
        $this->assertSame(1, Credit::where('company_id', $this->company->id)->count(), 'no se creó el clon');
    }

    #[Test]
    public function cloning_under_the_cap_succeeds(): void
    {
        $this->setupCompanyOnPlan(maxActiveCredits: 2);
        $credit = $this->activeCreditWithoutPayments(500000, 20, 10); // 1/2 activos

        $new = $this->cloneCredit($credit);

        $this->assertNotNull($new->id);
        $this->assertSame(2, Credit::where('company_id', $this->company->id)->count(), 'el clon se creó (2/2)');
    }
}
