<?php

declare(strict_types=1);

namespace App\Business\Report;

use App\Business\Inventory\Enums\InventoryDocumentStatus;
use App\Business\Purchase\Enums\PurchaseOrderStatus;
use App\Models\DberpProduct;
use App\Models\DberpSupplier;
use App\Support\DberpNumberFormatter;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;

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

    /**
     * @param  array<string, mixed>  $formState
     * @return array<string, mixed>
     */
    public function normalizeFilters(array $formState): array
    {
        $filters = [
            'supplier_id' => (int) ($formState['supplier_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),
            'status' => $formState['status'] ?? null,
            'business_time_from' => $formState['business_time_from'] ?? null,
            'business_time_until' => $formState['business_time_until'] ?? null,
        ];

        if ($filters['supplier_id'] <= 0) {
            unset($filters['supplier_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['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->purchaseReportService->purchaseOrdersFilteredQuery($filters);

        $exceptionBase = DB::table('dberp_purchase_exceptions');
        $this->applyExceptionDateScope($exceptionBase, $filters);
        if (! empty($filters['supplier_id'])) {
            $exceptionBase->where('supplier_id', (int) $filters['supplier_id']);
        }

        $returnBase = DB::table('dberp_purchase_return_orders')
            ->where('status', InventoryDocumentStatus::Effective->value);
        $this->applyReturnBusinessDateScope($returnBase, $filters);
        if (! empty($filters['supplier_id'])) {
            $returnBase->where('supplier_id', (int) $filters['supplier_id']);
        }

        return [
            'order_count' => (int) (clone $base)->count(),
            'total_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('total_amount')),
            'paid_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('paid_amount')),
            'unpaid_amount' => DberpNumberFormatter::money((float) (clone $base)->sum('unpaid_amount')),
            'total_quantity' => DberpNumberFormatter::quantity((clone $base)->sum('total_quantity')),
            'arrived_quantity' => DberpNumberFormatter::quantity((clone $base)->sum('total_arrived_quantity')),
            'remaining_arrival_quantity' => DberpNumberFormatter::quantity((clone $base)->sum('remaining_arrival_quantity')),
            'overdue_arrival_order_count' => (int) (clone $base)->whereIn('status', [
                PurchaseOrderStatus::Confirmed->value,
                PurchaseOrderStatus::PartiallyReceived->value,
            ])
                ->where('remaining_arrival_quantity', '>', 0)
                ->whereDate('expected_arrival_date', '<', now()->toDateString())
                ->count(),
            'partial_received_order_count' => (int) (clone $base)
                ->where('status', PurchaseOrderStatus::PartiallyReceived->value)
                ->count(),
            'fully_received_order_count' => (int) (clone $base)
                ->where('status', PurchaseOrderStatus::FullyReceived->value)
                ->count(),
            'exception_count' => (int) $exceptionBase->count(),
            'return_order_count' => (int) $returnBase->count(),
        ];
    }

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

        $rows = (clone $base)
            ->selectRaw('DATE(business_time) as stat_date')
            ->selectRaw('COUNT(*) as order_count')
            ->selectRaw('COALESCE(SUM(total_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 topSuppliers(array $filters, int $limit = 8): array
    {
        $models = $this->purchaseReportService->supplierRankingQuery($filters)->limit($limit)->get();

        $out = [];
        foreach ($models as $supplier) {
            /** @var DberpSupplier $supplier */
            $out[] = [
                'id' => (int) $supplier->getKey(),
                'name' => (string) $supplier->name,
                'amount' => DberpNumberFormatter::money((float) $supplier->getAttribute('purchase_amount')),
                'order_count' => (int) $supplier->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
    {
        $models = $this->purchaseReportService->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('purchase_amount')),
                'quantity' => DberpNumberFormatter::quantity($product->getAttribute('purchase_quantity')),
                'order_count' => (int) $product->getAttribute('purchase_order_count'),
            ];
        }

        return $out;
    }

    private function applyExceptionDateScope(Builder $query, array $filters): void
    {
        if (! empty($filters['business_time_from'])) {
            $query->whereDate('created_at', '>=', $filters['business_time_from']);
        }
        if (! empty($filters['business_time_until'])) {
            $query->whereDate('created_at', '<=', $filters['business_time_until']);
        }
    }

    private function applyReturnBusinessDateScope(Builder $query, array $filters): void
    {
        if (! empty($filters['business_time_from'])) {
            $query->whereDate('business_time', '>=', $filters['business_time_from']);
        }
        if (! empty($filters['business_time_until'])) {
            $query->whereDate('business_time', '<=', $filters['business_time_until']);
        }
    }
}
