<?php

declare(strict_types=1);

namespace Tests\Feature\Filament;

use App\Filament\Resources\Expenses\Pages\CreateExpenses;
use App\Models\Company;
use App\Models\CompanyFinancialSettings;
use App\Models\Expense;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Test;
use ReflectionMethod;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Regresión #69: la página Filament CreateExpenses debe aplicar la política de
 * aprobación por rol con la fuente única Expense::applyApprovalPolicyForCreator(),
 * idéntica a la PWA: admin/super_admin → APROBADO; supervisor/collector → PENDIENTE.
 *
 * Antes, mutateFormDataBeforeCreate solo trataba al no-admin; un admin podía
 * quedar PENDING (vía checkApprovalRequirements por umbral/categoría) y sin sus
 * campos approved_* fijados. La categoría 'refund' + umbral bajo reproduce ese
 * escenario: con el fix, el admin queda APROBADO (preApproved evita el override).
 *
 * Se ejercita handleRecordCreation (el método corregido) directamente, evitando
 * el harness de formularios de Filament.
 */
class CreateExpenseApprovalPolicyTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

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

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

        $plan = Plan::factory()->create(['has_pwa_access' => true]);
        $this->company = Company::factory()->create();

        Subscription::factory()->active()->create([
            'company_id' => $this->company->id,
            'plan_id' => $plan->id,
            'ends_at' => now()->addYear(),
        ]);

        // Umbral bajo: cualquier monto dispara la política por categoría.
        CompanyFinancialSettings::updateOrCreate(
            ['company_id' => $this->company->id],
            ['approval_threshold_amount' => '100.00'],
        );
    }

    private function makeUser(string $role): User
    {
        $user = User::factory()->create(['company_id' => $this->company->id]);
        $user->assignRole($role);

        return $user->fresh();
    }

    private function createExpenseAs(User $user): Expense
    {
        $this->actingAs($user);

        $page = new CreateExpenses;
        $method = new ReflectionMethod(CreateExpenses::class, 'handleRecordCreation');
        $method->setAccessible(true);

        /** @var Expense $expense */
        $expense = $method->invoke($page, [
            'company_id' => $this->company->id,
            'user_id' => $user->id,
            'amount' => '50000.00',
            // Categoría que requiere aprobación (refund) — con umbral bajo, el hook
            // checkApprovalRequirements pondría PENDING salvo que preApproved=true.
            'category' => 'refund',
            'expense_type' => 'operational',
            'operation_date' => today()->toDateString(),
        ]);

        return $expense->refresh();
    }

    #[Test]
    public function admin_created_expense_is_auto_approved_even_in_a_threshold_category(): void
    {
        $admin = $this->makeUser('admin');

        $expense = $this->createExpenseAs($admin);

        $this->assertFalse((bool) $expense->requires_approval);
        $this->assertSame(Expense::APPROVAL_APPROVED, $expense->approval_status);
        $this->assertSame($admin->id, $expense->approved_by_user_id);
        $this->assertNotNull($expense->approved_at);
    }

    #[Test]
    public function supervisor_created_expense_is_pending(): void
    {
        $supervisor = $this->makeUser('supervisor');

        $expense = $this->createExpenseAs($supervisor);

        $this->assertTrue((bool) $expense->requires_approval);
        $this->assertSame(Expense::APPROVAL_PENDING, $expense->approval_status);
        $this->assertNull($expense->approved_by_user_id);
        $this->assertNull($expense->approved_at);
    }

    #[Test]
    public function collector_created_expense_is_pending(): void
    {
        $collector = $this->makeUser('collector');

        $expense = $this->createExpenseAs($collector);

        $this->assertTrue((bool) $expense->requires_approval);
        $this->assertSame(Expense::APPROVAL_PENDING, $expense->approval_status);
    }
}
