<?php

declare(strict_types=1);

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

namespace App\Tools\Inventory;

use App\Business\Report\InventoryReportService;
use App\Models\DberpInventoryBalance;
use App\Services\DberpPermissionService;
use App\Support\DberpNumberFormatter;
use App\Tools\Contracts\Tool;
use App\Tools\Entity\EntityReference;
use App\Tools\Entity\EntityReferenceSchema;
use App\Tools\Entity\EntityType;
use App\Tools\Enums\ToolRisk;
use App\Tools\Support\ToolListSchema;
use App\Tools\Support\ToolPagedQuery;
use App\Tools\ToolExecutionContext;
use App\Tools\ToolMetadata;
use App\Tools\ToolPermission;

final class InventoryGetTool implements Tool
{
    public const SORTABLE = ['id', 'product_id', 'warehouse_id', 'realtime_quantity', 'available_quantity', 'last_moved_at'];

    public const DEFAULT_SORT = 'id';

    public function __construct(
        private readonly InventoryReportService $inventoryReportService,
    ) {}

    public function metadata(): ToolMetadata
    {
        return new ToolMetadata(
            name: 'inventory.get',
            version: 1,
            description: '查询当前库存余额（只读）。',
            permission: ToolPermission::permission(DberpPermissionService::VIEW_INVENTORY_BALANCE),
            risk: ToolRisk::ReadOnly,
            inputSchema: [
                'type' => 'object',
                'additionalProperties' => false,
                'properties' => [
                    'product_id' => ['type' => 'integer', 'minimum' => 1],
                    'product_sku_id' => ['type' => 'integer', 'minimum' => 1],
                    'warehouse_id' => ['type' => 'integer', 'minimum' => 1],
                    'category_id' => ['type' => 'integer', 'minimum' => 1],
                    ...ToolListSchema::paginationInputProperties(self::SORTABLE, self::DEFAULT_SORT),
                ],
            ],
            outputSchema: ToolListSchema::paginatedOutputSchema(EntityReferenceSchema::withRefs($this->itemSchema())),
            owner: 'core',
        );
    }

    /**
     * @param  array<string, mixed>  $input
     * @return array<string, mixed>
     */
    public function execute(ToolExecutionContext $context, array $input): array
    {
        $filters = $this->filtersFromInput($input);
        $query = $this->inventoryReportService->summaryQuery($filters);
        $page = ToolPagedQuery::paginate($query, $input, self::SORTABLE, self::DEFAULT_SORT);

        return [
            'items' => $page['rows']
                ->map(fn (mixed $row): array => $this->mapBalance($row))
                ->values()
                ->all(),
            ...ToolPagedQuery::meta($page),
        ];
    }

    /**
     * @param  array<string, mixed>  $input
     * @return array<string, mixed>
     */
    private function filtersFromInput(array $input): array
    {
        $filters = [];

        foreach (['product_id', 'product_sku_id', 'warehouse_id', 'category_id'] as $key) {
            if (($input[$key] ?? 0) > 0) {
                $filters[$key] = (int) $input[$key];
            }
        }

        return $filters;
    }

    /**
     * @return array<string, mixed>
     */
    private function mapBalance(mixed $row): array
    {
        if (! $row instanceof DberpInventoryBalance) {
            return [];
        }

        $product = $row->product;
        $warehouse = $row->warehouse;
        $sku = $row->productSku;

        $payload = [
            'id' => (int) $row->getKey(),
            'product_id' => (int) $row->product_id,
            'product_code' => (string) ($product?->code ?? ''),
            'product_name' => (string) ($product?->name ?? ''),
            'product_sku_id' => $row->product_sku_id === null ? null : (int) $row->product_sku_id,
            'sku_code' => $this->nullableString($sku?->sku_code),
            'spec_text' => $this->nullableString($sku?->spec_text),
            'warehouse_id' => (int) $row->warehouse_id,
            'warehouse_name' => (string) ($warehouse?->name ?? ''),
            'category_id' => $product?->category_id === null ? null : (int) $product->category_id,
            'category_name' => $this->nullableString($product?->category?->name),
            'realtime_quantity' => DberpNumberFormatter::quantity($row->realtime_quantity),
            'available_quantity' => DberpNumberFormatter::quantity($row->available_quantity),
            'occupied_quantity' => DberpNumberFormatter::quantity($row->occupied_quantity),
            'safety_stock' => DberpNumberFormatter::quantity($row->safety_stock),
            'last_moved_at' => $row->last_moved_at?->toIso8601String(),
        ];

        $refs = array_values(array_filter([
            EntityReference::tryMake(EntityType::Product, $row->product_id, $product?->code, $product?->name),
            EntityReference::tryMake(EntityType::Warehouse, $row->warehouse_id, $warehouse?->code, $warehouse?->name),
        ]));

        return EntityReference::attachMany($payload, ...$refs);
    }

    /**
     * @return array<string, mixed>
     */
    private function itemSchema(): array
    {
        return [
            'type' => 'object',
            'required' => [
                'id',
                'product_id',
                'product_code',
                'product_name',
                'warehouse_id',
                'warehouse_name',
                'realtime_quantity',
                'available_quantity',
                'occupied_quantity',
                'safety_stock',
            ],
            'additionalProperties' => false,
            'properties' => [
                'id' => ['type' => 'integer'],
                'product_id' => ['type' => 'integer'],
                'product_code' => ['type' => 'string'],
                'product_name' => ['type' => 'string'],
                'product_sku_id' => ['type' => 'integer'],
                'sku_code' => ['type' => 'string'],
                'spec_text' => ['type' => 'string'],
                'warehouse_id' => ['type' => 'integer'],
                'warehouse_name' => ['type' => 'string'],
                'category_id' => ['type' => 'integer'],
                'category_name' => ['type' => 'string'],
                'realtime_quantity' => ['type' => 'string'],
                'available_quantity' => ['type' => 'string'],
                'occupied_quantity' => ['type' => 'string'],
                'safety_stock' => ['type' => 'string'],
                'last_moved_at' => ['type' => 'string'],
            ],
        ];
    }

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

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

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