<?php
declare(strict_types=1);

namespace App\Modules\Inventory\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Inventory\Models\ItemBatch;

/**
 * FEFO (First Expiry First Out) batch allocation for expiry-tracked items.
 */
final class BatchService
{
    /**
     * Create a new batch when stock comes in (purchase, etc.).
     */
    public static function createBatch(array $data): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $batchId = $db->insert('item_batches', [
            'item_id'          => (int) $data['item_id'],
            'warehouse_id'     => (int) $data['warehouse_id'],
            'batch_number'     => $data['batch_number'],
            'expiry_date'      => $data['expiry_date'],
            'quantity'         => $data['quantity'],
            'initial_quantity' => $data['quantity'],
            'unit_cost'        => $data['unit_cost'] ?? null,
            'received_date'    => $data['received_date'] ?? date('Y-m-d'),
            'status'           => 'active',
            'notes'            => $data['notes'] ?? null,
            'created_by'       => $employee ? (int) $employee->id : null,
            'created_at'       => date('Y-m-d H:i:s'),
            'updated_at'       => date('Y-m-d H:i:s'),
        ]);

        Logger::info("Batch #{$batchId} created for item #{$data['item_id']}, qty: {$data['quantity']}, expiry: {$data['expiry_date']}");

        return $batchId;
    }

    /**
     * FEFO allocation: pick the earliest-expiring active batch(es) to fulfill a quantity.
     * Returns array of ['batch_id' => int, 'quantity' => string] allocations.
     *
     * @throws \RuntimeException if insufficient batch quantity
     */
    public static function allocateBatch(int $itemId, int $warehouseId, string $quantity): array
    {
        $batches = ItemBatch::getActiveBatches($itemId, $warehouseId);
        $remaining = $quantity;
        $allocations = [];

        foreach ($batches as $batch) {
            if (bccomp($remaining, '0', 3) <= 0) {
                break;
            }

            $available = (string) $batch['quantity'];
            $take = bccomp($available, $remaining, 3) >= 0 ? $remaining : $available;

            $allocations[] = [
                'batch_id' => (int) $batch['id'],
                'quantity' => $take,
            ];

            $remaining = bcsub($remaining, $take, 3);
        }

        if (bccomp($remaining, '0', 3) > 0) {
            throw new \RuntimeException(
                'كمية الدُفعات غير كافية — المطلوب: ' . $quantity . ' المتاح في الدفعات: ' . bcsub($quantity, $remaining, 3)
            );
        }

        return $allocations;
    }

    /**
     * Mark expired batches and dispatch alert events.
     * Called by the daily cron job.
     */
    public static function flagExpiredBatches(): int
    {
        $db = App::getInstance()->db();
        $expired = ItemBatch::getExpired();
        $count = 0;

        foreach ($expired as $batch) {
            $db->update('item_batches', [
                'status'     => 'expired',
                'updated_at' => date('Y-m-d H:i:s'),
            ], '`id` = ?', [(int) $batch['id']]);

            EventBus::dispatch('inventory.batch_expired', [
                'batch_id'     => (int) $batch['id'],
                'item_id'      => (int) $batch['item_id'],
                'warehouse_id' => (int) $batch['warehouse_id'],
                'expiry_date'  => $batch['expiry_date'],
                'quantity'     => $batch['quantity'],
            ]);

            $count++;
        }

        if ($count > 0) {
            Logger::info("Flagged {$count} expired batch(es)");
        }

        return $count;
    }

    /**
     * Get near-expiry batches and dispatch warnings.
     * Called by the daily cron job.
     */
    public static function alertNearExpiry(int $days = 30): int
    {
        $batches = ItemBatch::getNearExpiry($days);

        foreach ($batches as $batch) {
            EventBus::dispatch('inventory.expiry_warning', [
                'batch_id'     => (int) $batch['id'],
                'item_id'      => (int) $batch['item_id'],
                'warehouse_id' => (int) $batch['warehouse_id'],
                'expiry_date'  => $batch['expiry_date'],
                'days_left'    => (int) ((strtotime($batch['expiry_date']) - time()) / 86400),
            ]);
        }

        return count($batches);
    }
}
