<?php

namespace Modules\Vrc\Services\Finance;

use Modules\Vrc\Services\Contracts\TaxServiceContract;

/**
 * Server-side totals builder for repair orders and estimates (spec §4.1,
 * §6.4, §9). NEVER trusts posted totals — every line and document total is
 * recomputed here, with tax taken from the ERP tax engine via
 * TaxServiceContract. Pure/deterministic: feed it line arrays, get a totals
 * breakdown back (unit-testable with a stub tax service).
 *
 * Line shape (services & parts share it):
 *   ['qty'=>, 'unit_price'=>, 'discount'=>, 'discount_type'=>'fixed'|'percentage',
 *    'tax_rate_id'=>?int]
 * Per-line discount applies to (qty*unit_price); tax applies after discount.
 */
class TotalsBuilder
{
    public function __construct(protected TaxServiceContract $tax)
    {
    }

    /**
     * Compute a single line. Returns the line plus computed
     * line_subtotal / line_discount / line_tax / line_total.
     *
     * @return array{qty:float,unit_price:float,discount:float,discount_type:string,tax_rate_id:?int,tax_percent:float,line_subtotal:float,line_discount:float,line_tax:float,line_total:float}
     */
    public function line(int $businessId, array $line): array
    {
        $qty = (float) ($line['qty'] ?? 1);
        $unitPrice = (float) ($line['unit_price'] ?? 0);
        $discountType = ($line['discount_type'] ?? 'fixed') === 'percentage' ? 'percentage' : 'fixed';
        $discountInput = (float) ($line['discount'] ?? 0);
        $taxRateId = isset($line['tax_rate_id']) ? (int) $line['tax_rate_id'] : null;

        $subtotal = $this->round($qty * $unitPrice);

        $discount = $discountType === 'percentage'
            ? $this->round($subtotal * $discountInput / 100)
            : $this->round(min($discountInput, $subtotal));

        $taxable = max(0.0, $subtotal - $discount);
        $taxPercent = $this->tax->percentageFor($businessId, $taxRateId);
        $tax = $this->round($taxable * $taxPercent / 100);

        return [
            'qty'           => $qty,
            'unit_price'    => $unitPrice,
            'discount'      => $discountInput,
            'discount_type' => $discountType,
            'tax_rate_id'   => $taxRateId,
            'tax_percent'   => $taxPercent,
            'line_subtotal' => $subtotal,
            'line_discount' => $discount,
            'line_tax'      => $tax,
            'line_total'    => $this->round($taxable + $tax),
        ];
    }

    /**
     * Build document totals from many lines, plus an optional document-level
     * discount applied to the (post-line-discount) subtotal.
     *
     * @param  array<int,array>  $lines
     * @param  array{discount?:float,discount_type?:string}  $docDiscount
     * @return array{lines:array,subtotal:float,discount_total:float,tax_total:float,grand_total:float}
     */
    public function build(int $businessId, array $lines, array $docDiscount = []): array
    {
        $computed = [];
        $subtotal = 0.0;
        $lineDiscountTotal = 0.0;
        $taxTotal = 0.0;

        foreach ($lines as $line) {
            $c = $this->line($businessId, $line);
            $computed[] = $c;
            $subtotal += $c['line_subtotal'];
            $lineDiscountTotal += $c['line_discount'];
            $taxTotal += $c['line_tax'];
        }

        // Document-level discount on the net-of-line-discount subtotal.
        $netSubtotal = max(0.0, $subtotal - $lineDiscountTotal);
        $docDiscountType = ($docDiscount['discount_type'] ?? 'fixed') === 'percentage' ? 'percentage' : 'fixed';
        $docDiscountInput = (float) ($docDiscount['discount'] ?? 0);
        $docDiscountAmount = $docDiscountType === 'percentage'
            ? $this->round($netSubtotal * $docDiscountInput / 100)
            : $this->round(min($docDiscountInput, $netSubtotal));

        $discountTotal = $this->round($lineDiscountTotal + $docDiscountAmount);
        $taxTotal = $this->round($taxTotal);
        $grandTotal = $this->round(max(0.0, $subtotal - $discountTotal) + $taxTotal);

        return [
            'lines'          => $computed,
            'subtotal'       => $this->round($subtotal),
            'discount_total' => $discountTotal,
            'tax_total'      => $taxTotal,
            'grand_total'    => $grandTotal,
        ];
    }

    protected function round(float $value): float
    {
        return round($value, 2);
    }
}
