<?php

namespace GriffinForms\Includes\MergeTokens;

use GriffinForms\Includes\MergeTokens\Resolvers\FormResolver;
use GriffinForms\Includes\MergeTokens\Resolvers\FieldResolver;
use GriffinForms\Includes\MergeTokens\Resolvers\SubmissionResolver;

class Engine
{
    /** @var TokenParser */
    protected $parser;

    /** @var array<string,ResolverInterface> */
    protected $resolvers = [];

    public function __construct()
    {
        $this->parser = new TokenParser();
        $this->registerResolver('form', new FormResolver());
        $field_resolver = apply_filters('griffinforms_field_resolver', new FieldResolver());
        $this->registerResolver('field', $field_resolver instanceof ResolverInterface ? $field_resolver : new FieldResolver());
        $this->registerResolver('submission', new SubmissionResolver());
    }

    public function registerResolver(string $item_type, ResolverInterface $resolver): void
    {
        $key = strtolower($item_type);
        $this->resolvers[$key] = $resolver;
    }

    public function render(string $content, array $context = []): string
    {
        if ($content === '') {
            return $content;
        }

        $tokens = $this->parser->parse($content);
        if (empty($tokens)) {
            return $content;
        }

        $replacements = [];
        foreach ($tokens as $token) {
            $raw = $token['raw'] ?? '';
            if ($raw === '' || array_key_exists($raw, $replacements)) {
                continue;
            }
            $replacements[$raw] = $this->resolveToken($token['data'] ?? [], $context);
        }

        return strtr($content, $replacements);
    }

    protected function resolveToken(array $token, array $context): string
    {
        $item_type = strtolower((string) ($token['item_type'] ?? ''));
        $fallback = isset($token['fallback']) ? (string) $token['fallback'] : '';

        if ($item_type === '' || empty($this->resolvers[$item_type])) {
            return $fallback;
        }

        $resolved = $this->resolvers[$item_type]->resolve($token, $context);
        if ($resolved === null || $resolved === '') {
            return $fallback;
        }

        return $resolved;
    }
}
