<?php

declare(strict_types=1);

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

namespace App\Tools;

use App\Tools\Enums\ToolRisk;
use InvalidArgumentException;

final readonly class ToolMetadata
{
    public const NAME_PATTERN = '/^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/';

    public const OWNER_CORE = 'core';

    public const OWNER_TEST = 'test';

    /**
     * core / test / plugin:{alias}。插件 alias 与官方插件 identifier 风格一致。
     */
    public const OWNER_PATTERN = '/^(core|test|plugin:[a-z][a-z0-9_-]*)$/';

    public static function pluginOwner(string $alias): string
    {
        return 'plugin:'.trim($alias);
    }

    /**
     * @param  array<string, mixed>  $inputSchema
     * @param  array<string, mixed>  $outputSchema
     */
    public function __construct(
        public string $name,
        public int $version,
        public string $description,
        public ToolPermission $permission,
        public ToolRisk $risk,
        public array $inputSchema,
        public array $outputSchema,
        public string $owner,
    ) {
        if (preg_match(self::NAME_PATTERN, $this->name) !== 1) {
            throw new InvalidArgumentException(sprintf('Invalid tool name [%s]. Use domain.capability style.', $this->name));
        }

        if ($this->version < 1) {
            throw new InvalidArgumentException(sprintf('Tool [%s] version must be >= 1.', $this->name));
        }

        if (trim($this->description) === '') {
            throw new InvalidArgumentException(sprintf('Tool [%s] description must not be empty.', $this->name));
        }

        if (preg_match(self::OWNER_PATTERN, $this->owner) !== 1) {
            throw new InvalidArgumentException(sprintf(
                'Tool [%s] owner [%s] is invalid. Use core, test, or plugin:{alias}.',
                $this->name,
                $this->owner,
            ));
        }

        $this->assertObjectSchema($this->inputSchema, 'input_schema');
        $this->assertObjectSchema($this->outputSchema, 'output_schema');
    }

    /**
     * 未来 MCP / AI 选择所需的稳定 metadata 形状。不含 Tool 实例。
     *
     * @return array{
     *     name: string,
     *     version: int,
     *     description: string,
     *     permission: array{type: string, value: string},
     *     risk: string,
     *     input_schema: array<string, mixed>,
     *     output_schema: array<string, mixed>,
     *     owner: string
     * }
     */
    public function toArray(): array
    {
        return [
            'name' => $this->name,
            'version' => $this->version,
            'description' => $this->description,
            'permission' => [
                'type' => $this->permission->type->value,
                'value' => $this->permission->value,
            ],
            'risk' => $this->risk->value,
            'input_schema' => $this->inputSchema,
            'output_schema' => $this->outputSchema,
            'owner' => $this->owner,
        ];
    }

    /**
     * @param  array<string, mixed>  $schema
     */
    private function assertObjectSchema(array $schema, string $field): void
    {
        if (($schema['type'] ?? null) !== 'object') {
            throw new InvalidArgumentException(sprintf('Tool [%s] %s.type must be object.', $this->name, $field));
        }
    }
}
