<?php

use App\Services\Billing\InvoiceMath;

/**
 * The arithmetic, which on an invoice is the whole product.
 *
 * A document that does not add up is not a rounding artefact, it is a document
 * that does not add up — and the customer's calculator is what finds it.
 */
it('multiplies a fractional quantity without losing a cent', function () {
    // 12,5 hours at 100,00 €
    expect(InvoiceMath::lineNet(10000, 12500))->toBe(125000);

    // 0,25 months at 19,00 € — 4,75, not 4,74 and not 4,76
    expect(InvoiceMath::lineNet(1900, 250))->toBe(475);
});

it('applies a percentage to the sum, not line by line', function () {
    // Three lines at 3,33 € with 10 % off. Rounded per line, each discount is
    // 33 cents and the total is 99 — but 10 % of 9,99 € is 100 cents. One cent,
    // on the line the customer checks first.
    $lines = [
        ['unit_net_cents' => 333, 'quantity_milli' => 1000],
        ['unit_net_cents' => 333, 'quantity_milli' => 1000],
        ['unit_net_cents' => 333, 'quantity_milli' => 1000],
    ];

    $totals = InvoiceMath::totals($lines, ['type' => InvoiceMath::PERCENT, 'value' => -1000], 2000);

    expect($totals['lines_net'])->toBe(999)
        ->and($totals['adjustment'])->toBe(-100)
        ->and($totals['net'])->toBe(899);
});

it('treats a surcharge as the same operation with the other sign', function () {
    // One rule, not two flags somebody has to remember the meaning of.
    $lines = [['unit_net_cents' => 10000, 'quantity_milli' => 1000]];

    $discounted = InvoiceMath::totals($lines, ['type' => InvoiceMath::PERCENT, 'value' => -2000], 2000);
    $surcharged = InvoiceMath::totals($lines, ['type' => InvoiceMath::PERCENT, 'value' => 2000], 2000);

    expect($discounted['net'])->toBe(8000)
        ->and($surcharged['net'])->toBe(12000);
});

it('takes a fixed amount off as readily as a percentage', function () {
    $lines = [['unit_net_cents' => 10000, 'quantity_milli' => 1000]];

    $totals = InvoiceMath::totals($lines, ['type' => InvoiceMath::AMOUNT, 'value' => -1500], 2000);

    expect($totals['net'])->toBe(8500)
        ->and($totals['tax'])->toBe(1700)
        ->and($totals['gross'])->toBe(10200);
});

it('adds up: net plus tax is gross, on the figures actually printed', function () {
    // The assertion that matters, because it is the one a reader performs.
    $lines = [
        ['unit_net_cents' => 10000, 'quantity_milli' => 12000],
        ['unit_net_cents' => 31200, 'quantity_milli' => 20000],
    ];

    $totals = InvoiceMath::totals($lines, ['type' => InvoiceMath::PERCENT, 'value' => -2000], 2000);

    expect($totals['lines_net'])->toBe(744000)
        ->and($totals['adjustment'])->toBe(-148800)
        ->and($totals['net'])->toBe(595200)
        ->and($totals['tax'])->toBe(119040)
        ->and($totals['gross'])->toBe(714240)
        ->and($totals['net'] + $totals['tax'])->toBe($totals['gross']);
});

it('derives gross from net and never the other way round', function () {
    // 19,99 € net at 20 % is 23,99 €. Taking VAT back out of 23,99 gives 19,99
    // here and 19,992 elsewhere — which is why only one direction is offered.
    $totals = InvoiceMath::totals([['unit_net_cents' => 1999, 'quantity_milli' => 1000]], null, 2000);

    expect($totals['tax'])->toBe(400)
        ->and($totals['gross'])->toBe(2399);
});

it('formats money and quantities the way the document reads them', function () {
    expect(InvoiceMath::money(714240))->toBe('7.142,40')
        ->and(InvoiceMath::money(-14880))->toBe('-148,80')
        ->and(InvoiceMath::money(0))->toBe('0,00');

    // "12,000 Std." reads as a defect.
    expect(InvoiceMath::quantity(12000))->toBe('12')
        ->and(InvoiceMath::quantity(1500))->toBe('1,5')
        ->and(InvoiceMath::quantity(250))->toBe('0,25');
});

it('shows lines at full price and takes the discount off once, in euros', function () {
    // Two attempts got here. Applying one discount under the table left the
    // gross column summing to more than the total; printing each line already
    // reduced left a line reading 202,50 beside "Rabatt 10 %" with no way to
    // tell whether the discount was in that number or still to come. Full
    // price, a subtotal, one subtraction — a sum anybody can follow.
    $lines = [
        ['unit_net_cents' => 1900, 'quantity_milli' => 1000],
        ['unit_net_cents' => 9000, 'quantity_milli' => 2500, 'adjustment' => ['type' => InvoiceMath::PERCENT, 'value' => -1000]],
    ];

    $column = 0;

    foreach ($lines as $line) {
        $column += InvoiceMath::lineNet($line['unit_net_cents'], $line['quantity_milli']);
    }

    $totals = InvoiceMath::totals($lines, null, 2000);

    // The column the reader can add up on the page IS the subtotal.
    expect($column)->toBe($totals['subtotal'])
        ->and($totals['subtotal'])->toBe(24400)
        ->and($totals['adjustment'])->toBe(-2250)
        ->and($totals['net'])->toBe(22150)
        ->and($totals['net'] + $totals['tax'])->toBe($totals['gross']);
});

it('adds up discounts from several lines into one figure', function () {
    // Each line can carry its own, and the document shows their sum. Printing
    // three separate discount rows under the table is a summary block nobody
    // reads.
    $lines = [
        ['unit_net_cents' => 10000, 'quantity_milli' => 1000, 'adjustment' => ['type' => InvoiceMath::PERCENT, 'value' => -1000]],
        ['unit_net_cents' => 5000, 'quantity_milli' => 1000, 'adjustment' => ['type' => InvoiceMath::AMOUNT, 'value' => -500]],
        ['unit_net_cents' => 2000, 'quantity_milli' => 1000],
    ];

    $totals = InvoiceMath::totals($lines, null, 2000);

    expect($totals['subtotal'])->toBe(17000)
        ->and($totals['adjustment'])->toBe(-1500)
        ->and($totals['net'])->toBe(15500);
});
