<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Business\Workbench;

use App\Business\Inventory\InventoryGovernanceReminderService;
use App\Business\Purchase\PurchaseCollaborationReminderService;
use App\Business\Report\ComprehensiveSummaryAnalysisService;
use App\Business\Report\SalesAnalysisReportService;
use App\Business\Settlement\SettlementReminderService;
use App\Support\DberpNumberFormatter;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;

/**
 * 经营工作台顶部 KPI 与图表数据聚合；查询逻辑集中在此，Widget 与 Blade 仅消费结构化结果。
 */
final class BusinessOverviewMetricService
{
    private const SALES_TREND_DAYS = 7;

    /**
     * @var list<array<string, mixed>>|null
     */
    private ?array $cachedTopKpiMetrics = null;

    /**
     * @var array<string, mixed>|null
     */
    private ?array $cachedSalesTrend = null;

    /**
     * @var array<string, mixed>|null
     */
    private ?array $cachedPurchaseSalesComparison = null;

    /**
     * @var array<string, mixed>|null
     */
    private ?array $cachedInventoryWarningDistribution = null;

    /**
     * @var array<string, int>|null
     */
    private ?array $cachedInventoryReminderMetrics = null;

    public function __construct(
        private readonly ComprehensiveSummaryAnalysisService $comprehensiveSummaryAnalysisService,
        private readonly SalesAnalysisReportService $salesAnalysisReportService,
        private readonly PurchaseCollaborationReminderService $purchaseCollaborationReminderService,
        private readonly InventoryGovernanceReminderService $inventoryGovernanceReminderService,
        private readonly SettlementReminderService $settlementReminderService,
    ) {}

    /**
     * @return list<array{
     *     key: string,
     *     label: string,
     *     value: string,
     *     type: string,
     *     color: string,
     *     secondary_label: ?string,
     *     secondary_value: ?string,
     * }>
     */
    public function topKpiMetrics(): array
    {
        if ($this->cachedTopKpiMetrics !== null) {
            return $this->cachedTopKpiMetrics;
        }

        return $this->cachedTopKpiMetrics = $this->rememberWorkbenchMetric(
            'top_kpi',
            fn (): array => $this->computeTopKpiMetrics(),
        );
    }

    /**
     * @return list<array{
     *     key: string,
     *     label: string,
     *     value: string,
     *     type: string,
     *     color: string,
     *     secondary_label: ?string,
     *     secondary_value: ?string,
     * }>
     */
    private function computeTopKpiMetrics(): array
    {
        $monthFilters = $this->currentMonthFilters();
        $monthSalesSummary = $this->comprehensiveSummaryAnalysisService->salesSummary($monthFilters);
        $monthPurchaseSummary = $this->comprehensiveSummaryAnalysisService->purchaseSummary($monthFilters);
        $salesOperationalSummary = $this->salesAnalysisReportService->summary([]);
        $purchaseReminderMetrics = $this->purchaseCollaborationReminderService->overviewMetrics();
        $inventoryReminderMetrics = $this->inventoryReminderMetrics();
        $settlementReminderMetrics = $this->settlementReminderService->overviewMetrics();

        $pendingDeliveryCount = (int) ($salesOperationalSummary['confirmed_order_count'] ?? 0)
            + (int) ($salesOperationalSummary['partial_shipped_order_count'] ?? 0);

        $inventoryWarningCount = (int) ($inventoryReminderMetrics['low_stock_count'] ?? 0)
            + (int) ($inventoryReminderMetrics['open_inventory_exception_count'] ?? 0)
            + (int) ($inventoryReminderMetrics['pending_replenishment_count'] ?? 0);

        $monthReceivedAmount = DberpNumberFormatter::money($settlementReminderMetrics['month_receipt_amount'] ?? null);
        $monthPaidAmount = DberpNumberFormatter::money($settlementReminderMetrics['month_payment_amount'] ?? null);

        return [
            $this->kpiMetric(
                key: 'month_sales_amount',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_month_sales_amount',
                value: (string) ($monthSalesSummary['final_amount'] ?? '-'),
                type: 'money',
                color: 'primary',
            ),
            $this->kpiMetric(
                key: 'month_purchase_amount',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_month_purchase_amount',
                value: (string) ($monthPurchaseSummary['total_amount'] ?? '-'),
                type: 'money',
                color: 'warning',
            ),
            $this->kpiMetric(
                key: 'pending_delivery_count',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_pending_delivery_count',
                value: (string) max(0, $pendingDeliveryCount),
                type: 'count',
                color: 'primary',
            ),
            $this->kpiMetric(
                key: 'pending_receipt_count',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_pending_receipt_count',
                value: (string) max(0, (int) ($purchaseReminderMetrics['pending_arrival_order_count'] ?? 0)),
                type: 'count',
                color: 'warning',
            ),
            $this->kpiMetric(
                key: 'inventory_warning_count',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_inventory_warning_count',
                value: (string) max(0, $inventoryWarningCount),
                type: 'count',
                color: 'danger',
            ),
            $this->kpiMetric(
                key: 'receivable_amount',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_receivable_amount',
                value: DberpNumberFormatter::money($settlementReminderMetrics['receivable_total'] ?? null),
                type: 'money',
                color: 'success',
            ),
            $this->kpiMetric(
                key: 'payable_amount',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_payable_amount',
                value: DberpNumberFormatter::money($settlementReminderMetrics['payable_total'] ?? null),
                type: 'money',
                color: 'danger',
            ),
            $this->kpiMetric(
                key: 'month_cashflow',
                labelKey: 'dberp.widgets.business_overview_workbench.metric_month_cashflow',
                value: $monthReceivedAmount,
                type: 'money_pair',
                color: 'success',
                secondaryLabelKey: 'dberp.widgets.business_overview_workbench.metric_month_paid_amount',
                secondaryValue: $monthPaidAmount,
            ),
        ];
    }

    /**
     * @return array{labels: list<string>, datasets: list<array<string, mixed>>}
     */
    public function salesTrend(): array
    {
        if ($this->cachedSalesTrend !== null) {
            return $this->cachedSalesTrend;
        }

        return $this->cachedSalesTrend = $this->rememberWorkbenchMetric(
            'sales_trend',
            fn (): array => $this->computeSalesTrend(),
        );
    }

    /**
     * @return array{labels: list<string>, datasets: list<array<string, mixed>>}
     */
    private function computeSalesTrend(): array
    {
        $start = now()->subDays(self::SALES_TREND_DAYS - 1)->startOfDay();
        $rowsByDate = collect($this->comprehensiveSummaryAnalysisService->trendOverview([
            'business_time_from' => $start->toDateString(),
            'business_time_until' => now()->toDateString(),
        ]))->keyBy('date');

        $labels = [];
        $values = [];

        for ($offset = 0; $offset < self::SALES_TREND_DAYS; $offset++) {
            $date = $start->copy()->addDays($offset);
            $labels[] = $this->chartDayLabel($date);
            $row = $rowsByDate->get($date->toDateString());
            $values[] = $this->numericAmount($row['sales_amount'] ?? 0);
        }

        return [
            'labels' => $labels,
            'datasets' => [
                [
                    'label' => (string) __('dberp.widgets.business_overview_workbench.chart_sales_trend_dataset'),
                    'data' => $values,
                    'fill' => false,
                    'tension' => 0.3,
                ],
            ],
        ];
    }

    /**
     * @return array{labels: list<string>, datasets: list<array<string, mixed>>}
     */
    public function purchaseSalesComparison(): array
    {
        if ($this->cachedPurchaseSalesComparison !== null) {
            return $this->cachedPurchaseSalesComparison;
        }

        return $this->cachedPurchaseSalesComparison = $this->rememberWorkbenchMetric(
            'purchase_sales_comparison',
            fn (): array => $this->computePurchaseSalesComparison(),
        );
    }

    /**
     * @return array{labels: list<string>, datasets: list<array<string, mixed>>}
     */
    private function computePurchaseSalesComparison(): array
    {
        $start = now()->startOfMonth();
        $rowsByDate = collect($this->comprehensiveSummaryAnalysisService->trendOverview($this->currentMonthFilters()))
            ->keyBy('date');

        $labels = [];
        $salesValues = [];
        $purchaseValues = [];

        for ($cursor = $start->copy(); $cursor->lte(now()); $cursor->addDay()) {
            $labels[] = $this->chartDayLabel($cursor);
            $row = $rowsByDate->get($cursor->toDateString());
            $salesValues[] = $this->numericAmount($row['sales_amount'] ?? 0);
            $purchaseValues[] = $this->numericAmount($row['purchase_amount'] ?? 0);
        }

        return [
            'labels' => $labels,
            'datasets' => [
                [
                    'label' => (string) __('dberp.widgets.business_overview_workbench.chart_purchase_sales_sales_dataset'),
                    'data' => $salesValues,
                ],
                [
                    'label' => (string) __('dberp.widgets.business_overview_workbench.chart_purchase_sales_purchase_dataset'),
                    'data' => $purchaseValues,
                ],
            ],
        ];
    }

    /**
     * @return array{labels: list<string>, datasets: list<array<string, mixed>>}
     */
    public function inventoryWarningDistribution(): array
    {
        if ($this->cachedInventoryWarningDistribution !== null) {
            return $this->cachedInventoryWarningDistribution;
        }

        return $this->cachedInventoryWarningDistribution = $this->rememberWorkbenchMetric(
            'inventory_warning_distribution',
            fn (): array => $this->computeInventoryWarningDistribution(),
        );
    }

    /**
     * @return array{labels: list<string>, datasets: list<array<string, mixed>>}
     */
    private function computeInventoryWarningDistribution(): array
    {
        $metrics = $this->inventoryReminderMetrics();

        return [
            'labels' => [
                (string) __('dberp.widgets.business_overview_workbench.chart_inventory_low_stock'),
                (string) __('dberp.widgets.business_overview_workbench.chart_inventory_exception'),
                (string) __('dberp.widgets.business_overview_workbench.chart_inventory_replenishment'),
            ],
            'datasets' => [
                [
                    'data' => [
                        max(0, (int) ($metrics['low_stock_count'] ?? 0)),
                        max(0, (int) ($metrics['open_inventory_exception_count'] ?? 0)),
                        max(0, (int) ($metrics['pending_replenishment_count'] ?? 0)),
                    ],
                ],
            ],
        ];
    }

    /**
     * @template T
     *
     * @param  callable(): T  $callback
     * @return T
     */
    private function rememberWorkbenchMetric(string $segment, callable $callback): mixed
    {
        $cacheSeconds = max(0, (int) config('dberp.workbench.metrics_cache_seconds', 120));

        if ($cacheSeconds <= 0) {
            return $callback();
        }

        return Cache::remember(
            'dberp.workbench.metrics.'.$segment,
            now()->addSeconds($cacheSeconds),
            $callback,
        );
    }

    /**
     * @return array<string, int>
     */
    private function inventoryReminderMetrics(): array
    {
        return $this->cachedInventoryReminderMetrics ??= $this->inventoryGovernanceReminderService->overviewMetrics();
    }

    /**
     * @return array{business_time_from: string, business_time_until: string}
     */
    private function currentMonthFilters(): array
    {
        return [
            'business_time_from' => now()->startOfMonth()->toDateString(),
            'business_time_until' => now()->endOfMonth()->toDateString(),
        ];
    }

    private function chartDayLabel(Carbon $date): string
    {
        return $date->format('n/j');
    }

    private function numericAmount(mixed $formattedOrRaw): float
    {
        if (is_int($formattedOrRaw) || is_float($formattedOrRaw)) {
            return max(0, (float) $formattedOrRaw);
        }

        if (! is_string($formattedOrRaw)) {
            return 0.0;
        }

        $normalized = preg_replace('/[^\d.-]/', '', $formattedOrRaw);

        if (! is_string($normalized) || $normalized === '' || ! is_numeric($normalized)) {
            return 0.0;
        }

        return max(0, (float) $normalized);
    }

    /**
     * @return array{
     *     key: string,
     *     label: string,
     *     value: string,
     *     type: string,
     *     color: string,
     *     secondary_label: ?string,
     *     secondary_value: ?string,
     * }
     */
    private function kpiMetric(
        string $key,
        string $labelKey,
        string $value,
        string $type,
        string $color,
        ?string $secondaryLabelKey = null,
        ?string $secondaryValue = null,
    ): array {
        return [
            'key' => $key,
            'label' => (string) __($labelKey),
            'value' => $value === '' ? '-' : $value,
            'type' => $type,
            'color' => $color,
            'secondary_label' => $secondaryLabelKey !== null ? (string) __($secondaryLabelKey) : null,
            'secondary_value' => $secondaryValue === '' ? null : $secondaryValue,
        ];
    }
}
