<?php

namespace RTMMailComposer;

use Jawira\CaseConverter\Convert;
use RTMMailComposer\ConditionProcessor\ConditionFunctionInterface;
use RTMMailComposer\Model\CustomMail;

abstract class AbstractConditionSchemaBuilder
{


    /**
     * Generate the JSON schema for the conditions of an email.
     *
     * Conditions are generally based on the object type and the recipient type of the email.
     *
     * @param CustomMail $email
     * @return array|string[]
     */
    public function getSchema(CustomMail $email)
    {
        $functions = $this->getFunctionInstances($email);

        if (!$functions) {
            // No functions available for this email, so we don't need to show the conditions
            return [
                'title' => 'Conditions',
                'type' => 'null'
            ];
        }

        // generate schema
        // todo: add support for multiple conditions and condition-groups
        return [
            'title' => 'Conditions',
            'type' => 'array',
            'items' => [
                'type' => 'object',
                'properties' => [
                    'function' => [
                        'title' => 'Function',
                        'type' => 'string',
                        'enum' => array_map(fn($f) => $f->getName(), $functions),
                        'enumNames' => array_map([$this, 'functionNameHumanReadable'], $functions)
                    ]
                ],
                'required' => ['function'],
                'dependencies' => $this->generateDependenciesSchema($functions)
            ]
        ];
    }

    /**
     * Convert the function name to a human-readable string.
     *
     * @param ConditionFunctionInterface $function
     * @return string
     */
    private function functionNameHumanReadable(ConditionFunctionInterface $function): string
    {
        return (new Convert($function->getName()))->toSentence();
    }

    /**
     * Generate the dependencies schema for the given functions.
     *
     * @param ConditionFunctionInterface[] $functions
     * @return array[]
     */
    private function generateDependenciesSchema(array $functions)
    {
        return [
            'function' => [
                'oneOf' => array_map(function (ConditionFunctionInterface $f) {
                    $parameters = $f->getParameters();
                    $requiredParameters = [];
                    foreach ($parameters as $key => &$parameter) {
                        if (isset($parameter['required'])) {
                            $requiredParameters[] = $key;
                            unset($parameter['required']);
                        }
                    }

                    $required = $requiredParameters ? ['required' => $requiredParameters] : [];
                    $arguments = [];
                    if ($parameters) {
                        $arguments = ['arguments' => ['type' => 'object', 'properties' => $parameters] + $required];
                    }

                    $required = ['function'];
                    if ($arguments) {
                        $required[] = 'arguments';
                    }

                    return [
                        'properties' => [
                                'function' => [
                                    'enum' => [$f->getName()]
                                ]
                            ] + $arguments,
                        'required' => $required
                    ];
                }, $functions)
            ]
        ];
    }

    /**
     * @param CustomMail $email
     * @return ConditionFunctionInterface[]
     */
    protected abstract function getFunctionInstances(CustomMail $email): array;

}