<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Enums\WithdrawalType;
use App\Models\Company;
use App\Models\CompanyFinancialSettings;
use App\Models\Expense;
use App\Models\Partner;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use App\Services\PartnerInvestmentService;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Regresión: al aprobar un PartnerWithdrawal en Filament, el Expense vinculado
 * no debe quedar con approval_status='pending' en la BD aunque la categoría
 * (profit_withdrawal, capital_return, dividend_payment) tenga requiresApproval()=true.
 *
 * @see App\Services\PartnerInvestmentService::approveWithdrawal()
 * @see App\Models\Expense::checkApprovalRequirements()
 */
class PartnerWithdrawalApprovalTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $admin;

    private Partner $partner;

    private PartnerInvestmentService $service;

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

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

        $plan = Plan::factory()->create();
        $this->company = Company::factory()->create();

        Subscription::factory()->create([
            'company_id' => $this->company->id,
            'plan_id' => $plan->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');
        $this->admin = $this->admin->fresh();

        // Low threshold (100.00) so large withdrawals always trigger the approval check
        CompanyFinancialSettings::firstOrCreate(
            ['company_id' => $this->company->id],
            ['approval_threshold_amount' => '100.00']
        );

        $this->partner = Partner::create([
            'company_id' => $this->company->id,
            'name' => 'Socio Test',
            'status' => 'active',
            'user_id' => $this->admin->id,
            'joined_at' => Carbon::today(),
        ]);

        $this->service = app(PartnerInvestmentService::class);
    }

    #[Test]
    public function approved_withdrawal_creates_expense_with_approved_status(): void
    {
        $this->actingAs($this->admin);

        // Register a withdrawal above the approval threshold
        $withdrawal = $this->service->registerWithdrawal(
            partner: $this->partner,
            amount: '500000.00',
            type: WithdrawalType::PROFIT_WITHDRAWAL,
            operationDate: Carbon::today(),
            notes: 'Test withdrawal',
        );

        $this->assertEquals('pending', $withdrawal->approval_status);
        $this->assertNull($withdrawal->expense_id);

        // Approve it (the action performed in Filament)
        $approved = $this->service->approveWithdrawal($withdrawal, 'Approved by admin');

        $this->assertEquals('approved', $approved->approval_status);
        $this->assertNotNull($approved->expense_id);

        $expense = Expense::find($approved->expense_id);
        $this->assertNotNull($expense);

        $this->assertEquals(
            Expense::APPROVAL_APPROVED,
            $expense->approval_status,
            'El Expense del retiro aprobado no debería estar en estado pending'
        );
        $this->assertFalse(
            $expense->requires_approval,
            'El Expense del retiro aprobado no debería requerir aprobación adicional'
        );
    }

    #[Test]
    public function approved_withdrawal_expense_does_not_appear_in_pwa_pending_approvals(): void
    {
        $this->actingAs($this->admin);

        $withdrawal = $this->service->registerWithdrawal(
            partner: $this->partner,
            amount: '1000000.00',
            type: WithdrawalType::PROFIT_WITHDRAWAL,
            operationDate: Carbon::today(),
        );

        $this->service->approveWithdrawal($withdrawal);

        // Exact query from ApprovalController::pendingOperations()
        $pendingCount = Expense::where('company_id', $this->company->id)
            ->where('requires_approval', true)
            ->where('approval_status', Expense::APPROVAL_PENDING)
            ->count();

        $this->assertEquals(
            0,
            $pendingCount,
            'El Expense del retiro aprobado aparece como pendiente en la PWA'
        );
    }

    #[Test]
    public function pre_approved_flag_bypasses_approval_check_on_create(): void
    {
        $this->actingAs($this->admin);

        // Simulates the pattern used in executeDistribution()
        $expense = new Expense([
            'company_id' => $this->company->id,
            'amount' => '250000.00',
            'category' => 'dividend_payment',
            'expense_type' => 'dividend_payment',
            'operation_date' => Carbon::today(),
            'notes' => 'Distribución de utilidades',
            'user_id' => $this->admin->id,
            'affects_cash' => true,
            'affects_profit' => true,
            'reduces_capital' => false,
            'requires_approval' => false,
            'approval_status' => 'approved',
        ]);
        $expense->preApproved = true;
        $expense->save();

        $fresh = $expense->fresh();
        $this->assertEquals(Expense::APPROVAL_APPROVED, $fresh->approval_status);
        $this->assertFalse($fresh->requires_approval);
    }
}
