<?php
namespace boru\ai\Provider\Gemini;

class GeminiFunctionSchemaNormalizer
{
    const PLACEHOLDER_ARGUMENT_NAME = '__arg';

    /** @var array<string,bool> */
    protected $allowedKeys = array(
        'type' => true,
        'format' => true,
        'title' => true,
        'description' => true,
        'nullable' => true,
        'enum' => true,
        'properties' => true,
        'required' => true,
        'propertyOrdering' => true,
        'minProperties' => true,
        'maxProperties' => true,
        'minLength' => true,
        'maxLength' => true,
        'pattern' => true,
        'example' => true,
        'default' => true,
        'items' => true,
        'minimum' => true,
        'maximum' => true,
        'minItems' => true,
        'maxItems' => true,
        'anyOf' => true,
    );

    public function normalize(array $schema)
    {
        $normalized = $this->normalizeNode($schema);

        if (!isset($normalized['type']) || !is_string($normalized['type']) || $normalized['type'] === '') {
            $normalized['type'] = 'object';
        }

        if ($normalized['type'] === 'object' && empty($normalized['properties'])) {
            $normalized = $this->createPlaceholderObjectSchema($normalized);
        }

        return $normalized;
    }

    protected function normalizeNode(array $schema)
    {
        $normalized = array();
        $type = isset($schema['type']) && is_string($schema['type']) ? $schema['type'] : null;

        foreach ($schema as $key => $value) {
            if (!isset($this->allowedKeys[$key])) {
                continue;
            }

            if ($key === 'properties') {
                if ($type !== 'object' || !is_array($value)) {
                    continue;
                }

                $properties = array();
                foreach ($value as $propertyName => $propertySchema) {
                    if (!is_string($propertyName) || $propertyName === '' || !is_array($propertySchema)) {
                        continue;
                    }
                    $properties[$propertyName] = $this->normalizeNode($propertySchema);
                }
                $normalized[$key] = $properties;
                continue;
            }

            if ($key === 'items') {
                if ($type !== 'array' || !is_array($value)) {
                    continue;
                }
                $normalized[$key] = $this->normalizeNode($value);
                continue;
            }

            if ($key === 'required') {
                if ($type !== 'object' || !is_array($value)) {
                    continue;
                }

                $required = array();
                foreach ($value as $entry) {
                    if (is_string($entry) && $entry !== '') {
                        $required[] = $entry;
                    }
                }
                if (!empty($required)) {
                    $normalized[$key] = array_values(array_unique($required));
                }
                continue;
            }

            if ($key === 'anyOf') {
                if (!is_array($value)) {
                    continue;
                }

                $variants = array();
                foreach ($value as $variant) {
                    if (is_array($variant)) {
                        $variants[] = $this->normalizeNode($variant);
                    }
                }
                if (!empty($variants)) {
                    $normalized[$key] = $variants;
                }
                continue;
            }

            $normalized[$key] = $value;
        }

        if (isset($normalized['type']) && $normalized['type'] === 'object') {
            if (!isset($normalized['properties']) || !is_array($normalized['properties'])) {
                $normalized['properties'] = array();
            }

            if (empty($normalized['properties'])) {
                $normalized = $this->createPlaceholderObjectSchema($normalized);
            }
        }

        return $normalized;
    }

    protected function createPlaceholderObjectSchema(array $schema)
    {
        $description = isset($schema['description']) && is_string($schema['description'])
            ? trim($schema['description'])
            : '';

        $placeholderDescription = 'Placeholder argument required because Gemini rejects empty object parameter schemas.';
        if ($description !== '') {
            $placeholderDescription .= ' Original schema description: ' . $description;
        }

        $schema['properties'] = array(
            static::PLACEHOLDER_ARGUMENT_NAME => array(
                'type' => 'string',
                'description' => $placeholderDescription,
            ),
        );

        if (isset($schema['required']) && is_array($schema['required'])) {
            $required = array();
            foreach ($schema['required'] as $entry) {
                if (is_string($entry) && $entry !== '' && $entry !== static::PLACEHOLDER_ARGUMENT_NAME) {
                    $required[] = $entry;
                }
            }
            $schema['required'] = $required;
        }

        $schema['propertyOrdering'] = array(static::PLACEHOLDER_ARGUMENT_NAME);

        return $schema;
    }
}
