<?php

namespace Tests\Feature;

use App\Models\InactivityFeeCharge;
use App\Models\User;
use App\Services\InactivityFeeService;
use App\Services\WalletService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;

class InactivityFeeServiceTest extends TestCase
{
    use RefreshDatabase;

    public function test_monthly_inactivity_fee_is_idempotent_for_same_user_month(): void
    {
        Carbon::setTestNow('2026-03-11 10:00:00');

        $user = User::factory()->create([
            'last_activity_at' => Carbon::parse('2025-03-01 00:00:00'),
            'last_login_at' => Carbon::parse('2025-03-01 00:00:00'),
        ]);

        /** @var WalletService $walletService */
        $walletService = $this->app->make(WalletService::class);
        $walletService->credit($user, 80, 'seed-inactivity-wallet-001', 'Seed wallet', [], 'admin_adjust');

        /** @var InactivityFeeService $service */
        $service = $this->app->make(InactivityFeeService::class);
        $service->updateSettings([
            'enabled' => true,
            'inactivity_months' => 12,
            'monthly_fee_amount' => 100,
            'notify_before' => false,
            'notify_after' => false,
        ]);

        $preview = $service->preview([
            'as_of_date' => '2026-03-11',
        ]);
        $this->assertSame(1, (int) ($preview['inactive_users_due'] ?? 0));
        $this->assertSame(1, (int) ($preview['months_due_total'] ?? 0));

        $first = $service->run([
            'trigger' => 'manual',
            'as_of_date' => '2026-03-11',
        ]);

        $this->assertSame('completed', $first['status']);
        $this->assertSame(1, (int) ($first['months_due_total'] ?? 0));
        $this->assertSame(1, (int) ($first['partial_months'] ?? 0));
        $this->assertSame(0, (int) ($first['charged_months'] ?? 0));
        $this->assertSame(80.0, (float) ($first['total_deducted'] ?? 0));

        $this->assertDatabaseHas('inactivity_fee_charges', [
            'user_id' => $user->id,
            'charge_month' => '2026-03',
            'status' => 'partial',
        ]);
        $this->assertDatabaseHas('wallet_ledgers', [
            'user_id' => $user->id,
            'entry_type' => 'debit',
            'source' => 'maintenance_fee',
            'reference' => "wallet:maintenance_fee:{$user->id}:2026-03",
        ]);

        $user->refresh()->load('wallet');
        $this->assertSame('0.00', (string) $user->wallet->balance);

        $second = $service->run([
            'trigger' => 'manual',
            'as_of_date' => '2026-03-11',
        ]);

        $this->assertSame('completed', $second['status']);
        $this->assertSame(0, (int) ($second['months_due_total'] ?? 0));
        $this->assertSame(0.0, (float) ($second['total_deducted'] ?? 0));
        $this->assertSame(1, InactivityFeeCharge::query()->where('user_id', $user->id)->count());

        Carbon::setTestNow();
    }

    public function test_manual_run_can_catch_up_multiple_missed_months_without_negative_wallet(): void
    {
        Carbon::setTestNow('2026-03-11 10:00:00');

        $user = User::factory()->create([
            'last_activity_at' => Carbon::parse('2025-12-15 00:00:00'),
            'last_login_at' => Carbon::parse('2025-12-15 00:00:00'),
        ]);

        /** @var WalletService $walletService */
        $walletService = $this->app->make(WalletService::class);
        $walletService->credit($user, 250, 'seed-inactivity-wallet-002', 'Seed wallet', [], 'admin_adjust');

        /** @var InactivityFeeService $service */
        $service = $this->app->make(InactivityFeeService::class);
        $service->updateSettings([
            'enabled' => true,
            'inactivity_months' => 1,
            'monthly_fee_amount' => 100,
            'notify_before' => false,
            'notify_after' => false,
        ]);

        $result = $service->run([
            'trigger' => 'manual',
            'as_of_date' => '2026-03-11',
        ]);

        $this->assertSame('completed', $result['status']);
        $this->assertSame(3, (int) ($result['months_due_total'] ?? 0));
        $this->assertSame(2, (int) ($result['charged_months'] ?? 0));
        $this->assertSame(1, (int) ($result['partial_months'] ?? 0));
        $this->assertSame(250.0, (float) ($result['total_deducted'] ?? 0));

        $months = InactivityFeeCharge::query()
            ->where('user_id', $user->id)
            ->orderBy('charge_month')
            ->pluck('charge_month')
            ->all();

        $this->assertSame(['2026-01', '2026-02', '2026-03'], $months);

        $user->refresh()->load('wallet');
        $this->assertSame('0.00', (string) $user->wallet->balance);

        Carbon::setTestNow();
    }
}
