<?php

declare(strict_types=1);

namespace App\Business\Report;

use App\Business\Sales\Enums\SalesOrderStatus;
use App\Models\DberpCustomer;
use App\Models\DberpProduct;
use App\Models\DberpProductSku;
use App\Support\DberpNumberFormatter;
use App\Support\DberpReportSkuPresentation;

/**
 * 销售分析页聚合口径：与 {@see SalesReportService} 相同筛选条件，统计以销售订单头为主、排除未提交。
 */
final class SalesAnalysisReportService
{
    public function __construct(
        private readonly SalesReportService $salesReportService,
    ) {}

    /**
     * @param  array<string, mixed>  $formState
     * @return array<string, mixed>
     */
    public function normalizeFilters(array $formState): array
    {
        $filters = [
            'customer_id' => (int) ($formState['customer_id'] ?? 0),
            'handler_employee_id' => (int) ($formState['handler_employee_id'] ?? 0),
            'category_id' => (int) ($formState['category_id'] ?? 0),
            'product_id' => (int) ($formState['product_id'] ?? 0),
            'product_sku_id' => (int) ($formState['product_sku_id'] ?? 0),
            'sales_ranking_dimension' => (string) ($formState['sales_ranking_dimension'] ?? 'product'),
            'status' => $formState['status'] ?? null,
            'business_time_from' => $formState['business_time_from'] ?? null,
            'business_time_until' => $formState['business_time_until'] ?? null,
        ];

        if ($filters['customer_id'] <= 0) {
            unset($filters['customer_id']);
        }
        if ($filters['handler_employee_id'] <= 0) {
            unset($filters['handler_employee_id']);
        }
        if ($filters['category_id'] <= 0) {
            unset($filters['category_id']);
        }
        if ($filters['product_id'] <= 0) {
            unset($filters['product_id']);
        }
        if ($filters['product_sku_id'] <= 0) {
            unset($filters['product_sku_id']);
        }
        if ($filters['sales_ranking_dimension'] !== 'product_spec') {
            unset($filters['sales_ranking_dimension']);
        }
        if ($filters['status'] === '' || $filters['status'] === null) {
            unset($filters['status']);
        }
        if (empty($filters['business_time_from'])) {
            unset($filters['business_time_from']);
        }
        if (empty($filters['business_time_until'])) {
            unset($filters['business_time_until']);
        }

        return $filters;
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, int|string|float>
     */
    public function summary(array $filters): array
    {
        $base = $this->salesReportService->salesOrdersFilteredQuery($filters);

        return [
            'order_count' => (int) (clone $base)->count(),
            'final_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('final_amount')),
            'receivable_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('receivable_amount')),
            'received_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('received_amount')),
            'unreceived_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('unreceived_amount')),
            'total_quantity' => DberpNumberFormatter::quantity((clone $base)->sum('total_quantity')),
            'confirmed_order_count' => (int) (clone $base)->where('status', SalesOrderStatus::Confirmed->value)->count(),
            'partial_shipped_order_count' => (int) (clone $base)->where('status', SalesOrderStatus::PartiallyShipped->value)->count(),
            'fully_shipped_order_count' => (int) (clone $base)->where('status', SalesOrderStatus::FullyShipped->value)->count(),
        ];
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return list<array{date: string, order_count: int, amount: string}>
     */
    public function dailyTrend(array $filters): array
    {
        $base = $this->salesReportService->salesOrdersFilteredQuery($filters);

        $rows = (clone $base)
            ->selectRaw('DATE(business_time) as stat_date')
            ->selectRaw('COUNT(*) as order_count')
            ->selectRaw('COALESCE(SUM(final_amount), 0) as total_amount')
            ->groupBy('stat_date')
            ->orderBy('stat_date')
            ->get();

        $out = [];
        foreach ($rows as $row) {
            $out[] = [
                'date' => (string) $row->stat_date,
                'order_count' => (int) $row->order_count,
                'amount' => DberpNumberFormatter::money((float) $row->total_amount),
            ];
        }

        return $out;
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return list<array{id: int, name: string, amount: string, order_count: int}>
     */
    public function topCustomers(array $filters, int $limit = 8): array
    {
        $models = $this->salesReportService->customerRankingQuery($filters)->limit($limit)->get();

        $out = [];
        foreach ($models as $customer) {
            /** @var DberpCustomer $customer */
            $out[] = [
                'id' => (int) $customer->getKey(),
                'name' => (string) $customer->name,
                'amount' => DberpNumberFormatter::money((float) $customer->getAttribute('sales_amount')),
                'order_count' => (int) $customer->getAttribute('order_count'),
            ];
        }

        return $out;
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return list<array{id: int, name: string, amount: string, quantity: string, order_count: int}>
     */
    public function topProducts(array $filters, int $limit = 8): array
    {
        if (($filters['sales_ranking_dimension'] ?? null) === 'product_spec') {
            $models = $this->salesReportService->productSkuRankingQuery($filters)->limit($limit)->get();

            $out = [];
            foreach ($models as $sku) {
                /** @var DberpProductSku $sku */
                $productName = (string) ($sku->product?->name ?? '');
                $spec = DberpReportSkuPresentation::skuRecordSpecSummary($sku);
                $out[] = [
                    'id' => (int) $sku->getKey(),
                    'name' => trim($productName === '' ? $spec : $productName.' / '.$spec),
                    'amount' => DberpNumberFormatter::money((float) $sku->getAttribute('sales_amount')),
                    'quantity' => DberpNumberFormatter::quantity($sku->getAttribute('sales_quantity')),
                    'order_count' => (int) $sku->getAttribute('sales_order_count'),
                ];
            }

            return $out;
        }

        $models = $this->salesReportService->productRankingQuery($filters)->limit($limit)->get();

        $out = [];
        foreach ($models as $product) {
            /** @var DberpProduct $product */
            $out[] = [
                'id' => (int) $product->getKey(),
                'name' => (string) $product->name,
                'amount' => DberpNumberFormatter::money((float) $product->getAttribute('sales_amount')),
                'quantity' => DberpNumberFormatter::quantity($product->getAttribute('sales_quantity')),
                'order_count' => (int) $product->getAttribute('sales_order_count'),
            ];
        }

        return $out;
    }
}
