<?php

declare(strict_types=1);

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

namespace App\Business\Purchase;

use App\Models\DberpPurchaseOrder;
use App\Models\DberpPurchaseOrderItem;
use App\Support\DberpNumberFormatter;
use App\Tools\Entity\EntityReference;
use App\Tools\Entity\EntityType;
use App\Tools\Exceptions\ToolResourceNotFoundException;
use App\Tools\Support\ToolUniqueRecordLookup;

/**
 * 采购订单只读详情。不复用 PurchaseOrderOverviewService，避免把 Filament / 插件 UI 字段带进 Tool。
 */
final class PurchaseOrderReadQuery
{
    /**
     * @param  array<string, mixed>  $input
     * @return array<string, mixed>
     */
    public function find(array $input): array
    {
        $order = ToolUniqueRecordLookup::findOne(
            DberpPurchaseOrder::query()->with(['supplier', 'items']),
            $input,
        );

        if (! $order instanceof DberpPurchaseOrder) {
            throw ToolResourceNotFoundException::forLookup();
        }

        return $this->mapOrder($order);
    }

    /**
     * @return array<string, mixed>
     */
    private function mapOrder(DberpPurchaseOrder $order): array
    {
        $supplier = $order->supplier;

        $items = $order->items
            ->sortBy([
                ['sort_order', 'asc'],
                ['id', 'asc'],
            ])
            ->map(fn (DberpPurchaseOrderItem $item): array => $this->mapItem($item))
            ->values()
            ->all();

        $supplierPayload = [
            'id' => $supplier === null ? null : (int) $supplier->getKey(),
            'code' => $this->nullableString($supplier?->code),
            'name' => (string) ($supplier?->name ?? ''),
        ];

        return EntityReference::attach([
            'id' => (int) $order->getKey(),
            'code' => (string) $order->code,
            'status' => (string) $order->status,
            'business_time' => $order->business_time?->toIso8601String(),
            'expected_arrival_date' => $order->expected_arrival_date?->toDateString(),
            'handler_employee_id' => $order->handler_employee_id === null ? null : (int) $order->handler_employee_id,
            'supplier' => EntityReference::attachNested($supplierPayload, EntityType::Supplier),
            'amounts' => [
                'total_quantity' => DberpNumberFormatter::quantity($order->total_quantity),
                'total_amount' => DberpNumberFormatter::money($order->total_amount),
                'payable_amount' => DberpNumberFormatter::money($order->payable_amount),
                'paid_amount' => DberpNumberFormatter::money($order->paid_amount),
                'unpaid_amount' => DberpNumberFormatter::money($order->unpaid_amount),
                'total_arrived_quantity' => DberpNumberFormatter::quantity($order->total_arrived_quantity),
                'remaining_arrival_quantity' => DberpNumberFormatter::quantity($order->remaining_arrival_quantity),
            ],
            'items' => $items,
        ], EntityType::PurchaseOrder, (int) $order->getKey(), (string) $order->code);
    }

    /**
     * @return array<string, mixed>
     */
    private function mapItem(DberpPurchaseOrderItem $item): array
    {
        return EntityReference::attach([
            'id' => (int) $item->getKey(),
            'product_id' => (int) $item->product_id,
            'product_code' => (string) $item->product_code,
            'product_name' => (string) $item->product_name,
            'product_sku_id' => $item->product_sku_id === null ? null : (int) $item->product_sku_id,
            'sku_code' => $this->nullableString($item->sku_code),
            'spec_text' => $this->nullableString($item->spec_text),
            'unit_name' => $this->nullableString($item->unit_name),
            'quantity' => DberpNumberFormatter::quantity($item->quantity),
            'unit_price' => DberpNumberFormatter::price($item->unit_price),
            'line_amount' => DberpNumberFormatter::money($item->line_amount),
            'arrived_quantity' => DberpNumberFormatter::quantity($item->arrived_quantity),
            'remaining_arrival_quantity' => DberpNumberFormatter::quantity($item->remaining_arrival_quantity),
            'sort_order' => (int) ($item->sort_order ?? 0),
        ], EntityType::Product, (int) $item->product_id, (string) $item->product_code, (string) $item->product_name);
    }

    private function nullableString(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $normalized = trim((string) $value);

        return $normalized === '' ? null : $normalized;
    }
}
