<?php

declare(strict_types=1);

namespace App\Business\Inventory;

use App\Business\Inventory\Enums\InventoryExceptionType;
use App\Business\Inventory\Enums\InventoryExpiryStatus;
use App\Business\Inventory\Enums\InventoryReplenishmentSuggestionStatus;
use App\Business\Inventory\Enums\StocktakeDifferenceStatus;
use App\Models\DberpInventoryBatch;
use App\Models\DberpInventoryException;
use App\Models\DberpInventoryReplenishmentSuggestion;
use App\Models\DberpStocktakeDifference;
use Illuminate\Support\Facades\Cache;

final class InventoryGovernanceReminderService
{
    /**
     * @var array<string, int>|null
     */
    private ?array $cachedOverviewMetrics = null;

    public function __construct(
        private readonly InventoryExpiryService $expiryService,
        private readonly InventoryWarningService $warningService,
    ) {}

    /**
     * @return array<string, int>
     */
    public function overviewMetrics(): array
    {
        if ($this->cachedOverviewMetrics !== null) {
            return $this->cachedOverviewMetrics;
        }

        $cacheSeconds = max(0, (int) config('dberp.workbench.metrics_cache_seconds', 120));

        if ($cacheSeconds > 0) {
            /** @var array<string, int> $metrics */
            $metrics = Cache::remember(
                'dberp.workbench.inventory_governance_metrics',
                now()->addSeconds($cacheSeconds),
                fn (): array => $this->computeOverviewMetrics(),
            );

            return $this->cachedOverviewMetrics = $metrics;
        }

        return $this->cachedOverviewMetrics = $this->computeOverviewMetrics();
    }

    /**
     * @return list<array<string, string|int>>
     */
    public function reminderItems(): array
    {
        $metrics = $this->overviewMetrics();

        return [
            ['key' => 'expired_batch', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_expired_batch'), 'count' => $metrics['expired_batch_count'], 'amount' => ''],
            ['key' => 'serial_mismatch', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_serial_mismatch'), 'count' => $metrics['serial_mismatch_count'], 'amount' => ''],
            ['key' => 'near_expiry_batch', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_near_expiry_batch'), 'count' => $metrics['near_expiry_batch_count'], 'amount' => ''],
            ['key' => 'pending_stocktake_difference', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_pending_stocktake_difference'), 'count' => $metrics['pending_stocktake_difference_count'], 'amount' => ''],
            ['key' => 'pending_replenishment', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_pending_replenishment'), 'count' => $metrics['pending_replenishment_count'], 'amount' => ''],
            ['key' => 'low_stock', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_low_stock'), 'count' => $metrics['low_stock_count'], 'amount' => ''],
            ['key' => 'open_inventory_exception', 'title' => (string) __('dberp.widgets.inventory_workbench.reminder_open_inventory_exception'), 'count' => $metrics['open_inventory_exception_count'], 'amount' => ''],
        ];
    }

    /**
     * @return array<string, int>
     */
    private function computeOverviewMetrics(): array
    {
        return [
            'near_expiry_batch_count' => $this->nearExpiryBatchCount(),
            'expired_batch_count' => $this->expiredBatchCount(),
            'low_stock_count' => $this->warningService->query()->count(),
            'pending_replenishment_count' => DberpInventoryReplenishmentSuggestion::query()
                ->whereIn('status', [
                    InventoryReplenishmentSuggestionStatus::Pending->value,
                    InventoryReplenishmentSuggestionStatus::Reviewed->value,
                ])
                ->count(),
            'pending_stocktake_difference_count' => DberpStocktakeDifference::query()
                ->whereIn('status', [
                    StocktakeDifferenceStatus::Pending->value,
                    StocktakeDifferenceStatus::Reviewed->value,
                ])
                ->count(),
            'open_inventory_exception_count' => DberpInventoryException::query()->where('status', 'open')->count(),
            'serial_mismatch_count' => DberpInventoryException::query()
                ->where('status', 'open')
                ->where('exception_type', InventoryExceptionType::SerialBatchMismatch->value)
                ->count(),
            'today_stocktake_difference_count' => DberpStocktakeDifference::query()
                ->whereDate('created_at', now()->toDateString())
                ->count(),
        ];
    }

    private function nearExpiryBatchCount(): int
    {
        return $this->expiryService->constrainBatchQueryToExpiryStatus(
            DberpInventoryBatch::query()->where('available_quantity', '>', 0),
            InventoryExpiryStatus::NearExpiry->value,
        )->count();
    }

    private function expiredBatchCount(): int
    {
        return $this->expiryService->constrainBatchQueryToExpiryStatus(
            DberpInventoryBatch::query()->where('available_quantity', '>', 0),
            InventoryExpiryStatus::Expired->value,
        )->count();
    }
}
