<?php

declare(strict_types=1);

namespace App\Business\Inventory;

use App\Business\Inventory\Enums\InventoryReplenishmentSuggestionStatus;
use App\Models\DberpInventoryReplenishmentSuggestion;
use App\Models\DberpInventoryRule;
use App\Services\DberpAuditService;
use Illuminate\Database\Eloquent\Builder;

final class InventoryReplenishmentSuggestionService
{
    public function __construct(
        private readonly InventoryRuleEvaluatorService $ruleEvaluatorService,
        private readonly InventoryExceptionService $exceptionService,
        private readonly DberpAuditService $auditService,
    ) {}

    public function query(): Builder
    {
        return DberpInventoryReplenishmentSuggestion::query()->with(['product', 'warehouse']);
    }

    public function generateForRule(DberpInventoryRule $rule): ?DberpInventoryReplenishmentSuggestion
    {
        if (! $rule->is_enabled || ! $rule->replenishment_enabled) {
            return null;
        }

        $metrics = $this->ruleEvaluatorService->metrics($rule);

        if (! ((bool) $metrics['is_reorder_due'])) {
            return null;
        }

        $suggestedQuantity = $this->ruleEvaluatorService->suggestedQuantity($rule);

        $suggestion = DberpInventoryReplenishmentSuggestion::query()->updateOrCreate(
            [
                'product_id' => $rule->product_id,
                'warehouse_id' => $rule->warehouse_id,
                'status' => InventoryReplenishmentSuggestionStatus::Pending->value,
            ],
            [
                'system_suggested_quantity' => $suggestedQuantity,
                'adjusted_quantity' => $suggestedQuantity,
                'trigger_reason' => '已触发订货点',
                'generated_at' => now(),
                'metric_snapshot' => $metrics,
                'remark' => '由库存规则基础服务生成。',
            ],
        );

        $this->auditService->log('dberp.inventory_replenishment_suggestion.generated', $suggestion, null, [
            'product_id' => $suggestion->product_id,
            'warehouse_id' => $suggestion->warehouse_id,
            'system_suggested_quantity' => $suggestion->system_suggested_quantity,
        ]);

        $this->exceptionService->syncReplenishmentSuggestion($suggestion);

        return $suggestion;
    }

    public function refreshForRule(DberpInventoryRule $rule): ?DberpInventoryReplenishmentSuggestion
    {
        return $this->generateForRule($rule);
    }

    public function markReviewed(DberpInventoryReplenishmentSuggestion $suggestion, ?string $remark = null): DberpInventoryReplenishmentSuggestion
    {
        return $this->transitionStatus($suggestion, InventoryReplenishmentSuggestionStatus::Reviewed, $remark);
    }

    public function markProcessed(DberpInventoryReplenishmentSuggestion $suggestion, ?string $remark = null): DberpInventoryReplenishmentSuggestion
    {
        return $this->transitionStatus($suggestion, InventoryReplenishmentSuggestionStatus::Processed, $remark, true);
    }

    public function markIgnored(DberpInventoryReplenishmentSuggestion $suggestion, ?string $remark = null): DberpInventoryReplenishmentSuggestion
    {
        return $this->transitionStatus($suggestion, InventoryReplenishmentSuggestionStatus::Ignored, $remark);
    }

    public function generateForAllActiveRules(): int
    {
        $count = 0;

        DberpInventoryRule::query()
            ->where('is_enabled', true)
            ->where('replenishment_enabled', true)
            ->orderBy('sort_order')
            ->orderBy('id')
            ->get()
            ->each(function (DberpInventoryRule $rule) use (&$count): void {
                if ($this->generateForRule($rule) !== null) {
                    $count++;
                }
            });

        return $count;
    }

    private function transitionStatus(
        DberpInventoryReplenishmentSuggestion $suggestion,
        InventoryReplenishmentSuggestionStatus $status,
        ?string $remark = null,
        bool $processed = false,
    ): DberpInventoryReplenishmentSuggestion {
        $before = $suggestion->only(['status', 'remark', 'processed_at']);

        $this->auditService->logSemanticSave(
            'dberp.inventory_replenishment_suggestion.status_changed',
            $suggestion,
            function () use ($suggestion, $status, $remark, $processed): void {
                $suggestion->forceFill([
                    'status' => $status->value,
                    'processed_at' => $processed ? now() : $suggestion->processed_at,
                    'remark' => $remark ?: $suggestion->remark,
                ])->save();
            },
            $before,
            [
                'status' => $status->value,
                'processed' => $processed,
            ],
        );

        $this->exceptionService->syncReplenishmentSuggestion($suggestion);

        return $suggestion->refresh();
    }
}
