<?php

namespace App\Support\Invoice;

use App\Models\Sales\InvoiceItem;

/**
 * One printable row. Flattens everything a line can carry — serial and batch
 * identifiers, unit of measure, both discount styles — into plain scalars the
 * blades can render without touching a relation.
 */
final class InvoiceDocumentLine
{
    public function __construct(
        public readonly string $name,
        public readonly ?string $sku,
        public readonly float $quantity,
        public readonly ?string $unit,
        public readonly float $unitPrice,
        public readonly float $grossTotal,
        public readonly ?string $discountType,
        public readonly float $discountValue,
        public readonly float $discountAmount,
        public readonly float $lineTotal,
        public readonly float $vatRate,
        public readonly float $vatAmount,
        public readonly ?string $serialNumber = null,
        public readonly ?string $batchNumber = null,
        public readonly ?string $batchExpiry = null,
        public readonly ?string $bundleName = null,
        public readonly ?string $bundleGroupKey = null,
    ) {}

    public static function fromItem(InvoiceItem $item): self
    {
        return new self(
            name: (string) $item->product_name,
            sku: $item->product_sku,
            quantity: (float) $item->quantity,
            unit: $item->unit?->code ?? $item->unit?->name,
            unitPrice: (float) $item->unit_price,
            // gross_total predates nothing on legacy rows, so fall back to the
            // line total when it was never written.
            grossTotal: (float) ($item->gross_total ?? $item->line_total),
            discountType: $item->discount_type,
            discountValue: (float) $item->discount_value,
            discountAmount: (float) $item->discount_amount,
            lineTotal: (float) $item->line_total,
            vatRate: (float) $item->vat_rate,
            vatAmount: (float) $item->vat_amount,
            serialNumber: $item->serial?->serial_number,
            // `internal_batch_number` is the lot's only number. This read
            // used to name `batch_number`, a column stock_batches never had,
            // so the batch line on every printed invoice came out blank.
            batchNumber: $item->stockBatch?->internal_batch_number,
            batchExpiry: $item->stockBatch?->expiry_date?->format('d M Y'),
            bundleName: $item->bundle?->name,
            bundleGroupKey: $item->bundle_group_key,
        );
    }

    /**
     * How the discount is shown in the row — "10%" reads very differently from
     * a flat amount, and a customer disputing a bill needs to see which it was.
     */
    public function discountLabel(): ?string
    {
        if ($this->discountAmount <= 0) {
            return null;
        }

        if ($this->discountType === 'percentage') {
            return rtrim(rtrim(number_format($this->discountValue, 2, '.', ''), '0'), '.').'%';
        }

        return 'Less';
    }

    public function hasDiscount(): bool
    {
        return $this->discountAmount > 0;
    }

    public function quantityLabel(): string
    {
        $quantity = rtrim(rtrim(number_format($this->quantity, 4, '.', ''), '0'), '.');

        return $this->unit !== null ? $quantity.' '.$this->unit : $quantity;
    }

    /**
     * Traceability identifiers, which a serialised or batch-tracked sale must
     * print for warranty and recall purposes.
     *
     * @return list<string>
     */
    public function traceLines(): array
    {
        $lines = [];

        if ($this->serialNumber !== null) {
            $lines[] = 'S/N '.$this->serialNumber;
        }

        if ($this->batchNumber !== null) {
            $lines[] = 'Batch '.$this->batchNumber.($this->batchExpiry !== null ? ' · exp '.$this->batchExpiry : '');
        }

        return $lines;
    }
}
