<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\Tools;

use Closure;
use Plugin\tnt_mcp\src\Mcp\Commerce\ShippingMethodReader;
use Plugin\tnt_mcp\src\Mcp\Contracts\Tool;
use Plugin\tnt_mcp\src\Mcp\Mode\EndpointClass;
use RuntimeException;

/**
 * READ tool: one shipping method as the whole domain object — price tiers, ZIP-code increments,
 * per-language texts, payment surcharges, customer-group and shipping-class restrictions, and how
 * it is taxed.
 *
 * Takes the id or the name, because the shop owner reads out the name and the controller wants the
 * id; making the model resolve that itself is a wasted turn (same reasoning as `get_order`).
 */
final class GetShippingMethod implements Tool
{
    /** @param Closure(): ShippingMethodReader $readerFactory must not touch the DB until run() */
    public function __construct(private readonly Closure $readerFactory)
    {
    }

    public function name(): string
    {
        return 'get_shipping_method';
    }

    public function description(): string
    {
        return 'Get one shipping method in full: price tiers, ZIP-code surcharges, countries, '
            . 'payment surcharges, languages and tax basis.';
    }

    /** @return array<string,mixed> */
    public function inputSchema(): array
    {
        return [
            'type'       => 'object',
            'properties' => [
                'shippingMethodId' => [
                    'type'        => 'integer',
                    'minimum'     => 1,
                    'description' => 'Internal id (tversandart.kVersandart). Pass this or name.',
                ],
                'name'             => [
                    'type'        => 'string',
                    'description' => 'Exact shipping method name (tversandart.cName), '
                        . 'as shown in the admin overview.',
                ],
            ],
            'additionalProperties' => false,
        ];
    }

    public function isWrite(): bool
    {
        return false;
    }

    public function scope(): string
    {
        return 'shop:read';
    }

    public function endpointClass(): EndpointClass
    {
        return EndpointClass::Reversible;
    }

    /**
     * @param array<string,mixed> $args
     * @return array<string,mixed>
     */
    public function run(array $args): array
    {
        $id   = $args['shippingMethodId'] ?? null;
        $name = $args['name'] ?? null;
        if ($id !== null && (!\is_int($id) || $id < 1)) {
            throw new RuntimeException(
                'get_shipping_method: "shippingMethodId" must be a positive whole number.'
            );
        }
        if ($name !== null && !\is_string($name)) {
            throw new RuntimeException('get_shipping_method: "name" must be a string.');
        }
        $name = \is_string($name) ? \trim($name) : null;
        if ($id === null && ($name === null || $name === '')) {
            throw new RuntimeException(
                'get_shipping_method: pass either "shippingMethodId" or "name".'
            );
        }

        $method = ($this->readerFactory)()->method($id ?? (string)$name);
        if ($method === null) {
            return [
                'found'  => false,
                'reason' => $id !== null
                    ? 'No shipping method has id ' . $id . '.'
                    : 'No shipping method is named "' . $name . '". Names are matched exactly — '
                        . 'call list_shipping_methods for the spelling.',
            ];
        }

        return ['found' => true] + $method;
    }
}
