<?php

use App\Enums\BillingStatus;
use App\Models\Bank;
use App\Models\Billing;
use App\Models\Group;
use App\Models\User;
use App\Services\InvoiceService;

use function Pest\Laravel\actingAs;

beforeEach(function () {
    Group::firstOrCreate(['name' => Group::ADMIN]);
    Group::firstOrCreate(['name' => Group::MEMBERS]);
});

test('invoice number follows DDMMYYYY + 4 digit sequence', function () {
    $service = app(InvoiceService::class);
    $first = $service->generate();

    // Persist the first so the sequence increments.
    Billing::create([
        'no_invoice' => $first,
        'jumlah_credit' => 100,
        'rate' => 100,
        'total' => 10000,
        'status' => BillingStatus::Pending,
        'user_id' => makeMember()->id,
    ]);

    $second = $service->generate();

    expect(strlen($first))->toBe(12)
        ->and(substr($first, 0, 8))->toBe(now()->format('dmY'))
        ->and(substr($first, -4))->toBe('0001')
        ->and(substr($second, -4))->toBe('0002');
});

test('admin can create a billing invoice', function () {
    $admin = makeAdmin();
    $member = makeMember();
    $bank = Bank::factory()->create();

    $response = actingAs($admin)->post(route('billings.store'), [
        'jumlah_credit' => 100000,
        'rate' => 1,
        'bank_id' => $bank->id,
        'user_id' => $member->id,
    ]);

    $response->assertRedirect(route('billings.index'));

    $billing = Billing::first();
    expect($billing->no_invoice)->not->toBeEmpty()
        ->and($billing->total)->toBe(100000)
        ->and($billing->status)->toBe(BillingStatus::Pending)
        ->and($billing->user_id)->toBe($member->id);
});

test('topup below minimum 50000 is rejected', function () {
    $admin = makeAdmin();
    $member = makeMember();

    $response = actingAs($admin)->post(route('billings.store'), [
        'jumlah_credit' => 10000,
        'rate' => 1,
        'user_id' => $member->id,
    ]);

    $response->assertSessionHasErrors('jumlah_credit');
    expect(Billing::count())->toBe(0);
});

test('confirming billing adds credit and records In mutation', function () {
    $admin = makeAdmin();
    $member = makeMember();

    $billing = Billing::factory()->create([
        'user_id' => $member->id,
        'jumlah_credit' => 500,
        'status' => BillingStatus::Pending,
    ]);

    expect($member->credit)->toBe(0);

    actingAs($admin)->get(route('billings.confirm', $billing))
        ->assertRedirect(route('billings.index'));

    $member->refresh();
    $billing->refresh();

    expect($member->credit)->toBe(500)
        ->and($billing->status)->toBe(BillingStatus::Lunas)
        ->and($billing->paid_at)->not->toBeNull();

    $history = $member->creditHistory()->first();
    expect($history)->not->toBeNull()
        ->and($history->credit)->toBe(500)
        ->and($history->jenis->value)->toBe('In')
        ->and($history->invoice)->toBe($billing->no_invoice);
});

test('confirming a billing twice does not double credit (idempotent)', function () {
    $admin = makeAdmin();
    $member = makeMember();

    $billing = Billing::factory()->create([
        'user_id' => $member->id,
        'jumlah_credit' => 500,
        'status' => BillingStatus::Pending,
    ]);

    actingAs($admin)->get(route('billings.confirm', $billing));
    actingAs($admin)->get(route('billings.confirm', $billing));

    $member->refresh();

    expect($member->credit)->toBe(500)
        ->and($member->creditHistory()->count())->toBe(1);
});

test('member can view only their own billings', function () {
    $member = makeMember();
    $other = makeMember();

    Billing::factory()->create(['user_id' => $member->id, 'no_invoice' => 'OWNED']);
    Billing::factory()->create(['user_id' => $other->id, 'no_invoice' => 'OTHER']);

    $response = actingAs($member)->get(route('billings.index'));

    $invoices = collect($response->inertiaProps()['billings']['data'])->pluck('no_invoice');

    expect($invoices)->toContain('OWNED')
        ->and($invoices)->not->toContain('OTHER');
});

test('member cannot create billing for another user', function () {
    $member = makeMember();
    $other = makeMember();

    // Member is allowed to create billing but only for themselves.
    actingAs($member)->post(route('billings.store'), [
        'jumlah_credit' => 50000,
        'rate' => 1,
        'user_id' => $other->id, // attempt to target another user
    ]);

    // The billing should be created for the member, NOT $other.
    $billing = Billing::first();
    expect($billing->user_id)->toBe($member->id);
});
