<?php

namespace Modules\Inventory\Services;

use Illuminate\Support\Collection;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Inventory\Models\Product;
use Modules\Inventory\Models\ProductUnitConversion;

class PurchaseRequisitionCalculationService
{
    public function __construct(
        protected VatCalculationService $vatService
    ){ }

    private function validateDuplicateProducts(array $items): void
    {
        $duplicates = collect($items)->pluck('product_id')->duplicates();

        if ($duplicates->isNotEmpty()) {
            ApiValidationException::message('Duplicate products are not allowed in the same purchase requisition.');
        }
    }

    public function calculate(array $items): array
    {
        $this->validateDuplicateProducts($items);

        $products = $this->loadProducts($items);

        $subtotal = 0;
        $calculatedItems = [];

        foreach ($items as $item) {
            $product = $products[$item['product_id']];
            
            $calculated = $this->calculateItem(
                product: $product,
                item: $item
            );

            $calculatedItems[] = $calculated;

            $subtotal += $calculated['line_total'];
        }

        return [
            'items' => $calculatedItems,
            'subtotal' => round($subtotal, 2),
            'total_discount' => 0,
            'taxable_amount' => round($subtotal, 2),
            'total_vat' => 0,
            'grand_total' => round($subtotal, 2),
        ];
    }

    private function loadProducts(array $items): Collection
    {
        $productIds = collect($items)->pluck('product_id')->unique();
        $unitIds = collect($items)->pluck('unit_id')->unique();

        return Product::query()
            ->with(['unitConversions' => fn ($query) =>
                $query->whereIn('from_unit_id', $unitIds)
            ])
            ->whereIn('id', $productIds)
            ->get()
            ->keyBy('id');
    }

    private function calculateItem(Product $product, array $item): array
    {
        $qty = (float) $item['qty'];
        $unitPrice = (float) $item['unit_price'];
        $baseQty = $this->calculateBaseQty(
            product: $product,
            unitId: $item['unit_id'],
            qty: $qty
        );

        $lineTotal = $this->calculateLineTotal(
            qty: $qty,
            unitPrice: $unitPrice
        );

        return [
            'product_id' => $product->id,
            'unit_id' => $item['unit_id'],
            'qty' => $qty,
            'base_qty' => $baseQty,
            'unit_price' => $unitPrice,
            'line_total' => $lineTotal,
            'remarks' => $item['remarks'] ?? null,
        ];
    }

    private function calculateLineTotal(float $qty, float $unitPrice): float
    {
        return round($qty * $unitPrice, 2);
    }

    private function calculateBaseQty(Product $product, int $unitId, float $qty): float
    {
        if ($unitId == $product->base_unit_id) {
            return round($qty, 6);
        }

        $conversion = $this->getConversion(
            product: $product,
            unitId: $unitId
        );

        if (!$conversion) {
            ApiValidationException::errors([
                'unit_id' => [
                    'Invalid unit conversion.'
                ]
            ]);
        }

        return round(($qty * $conversion->to_qty) / $conversion->from_qty, 6);
    }

    private function getConversion(Product $product, int $unitId): ?ProductUnitConversion
    {
        return $product->unitConversions->firstWhere('from_unit_id', $unitId);
    }
}
