<?php

namespace RTMMailComposer\Renderer;

use RTMMailComposer\ConditionProcessor\ConditionFunctionInterface;
use RTMMailComposer\ConditionProcessor\ConditionProcessor;
use RTMMailComposer\ConditionProcessor\ConditionProcessorInterface;
use RTMMailComposer\ContextBuilder\ContextBuilderRegistry;
use RTMMailComposer\Model\CustomMail;
use RTMMailComposer\Model\EmailSection;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;

class TwigEmailRenderer extends AbstractEmailRenderer
{
    protected ?ConditionProcessorInterface $conditionProcessor;

    public function __construct(ContextBuilderRegistry $contextBuilder, protected Environment $twig)
    {
        parent::__construct($contextBuilder);
    }

    /**
     * @inheritDoc
     */
    public function render(CustomMail $mail, object $mailObject, array $initialContext = []): string
    {
        $objectType = $this->initConditionProcessor($mail);

        //prepare context to be used in twig rendering
        $context = $this->contextBuilder->prepareContext($mail, $mailObject, [$objectType => $mailObject, ...$initialContext]);

        return $this->renderSections($mail, $context);
    }


    /**
     * Renders all the sections for a given email
     * @param CustomMail $mail Mail to be rendered
     * @param array $context The context to be used during rendering
     *
     * @return string The rendered output for all the sections
     */
    public function renderSections(CustomMail $mail, array $context): string
    {
        $sections = $mail->getSections();
        usort($sections, fn(EmailSection $a, EmailSection $b) => $a->getSortOrder() <=> $b->getSortOrder());

        $renderedSections = [];
        // render sections
        foreach ($sections as $section) {
            if (!$section->isEnable() || !$this->matchesCondition($section, $context, $mail)) {
                continue;
            }
            $output = "";
            if ($section->isShowTitle()) {
                $output .= "<h4>{$section->getTitle()}</h4>";
            }
            $renderedSections[] = $output . $this->renderSection($section, $context);
        }

        return join("", $renderedSections);
    }

    /**
     * @inheritDoc
     */
    public function renderSection(EmailSection $section, array $context): string
    {
        try {
            $template = $this->twig->createTemplate($section->getText());
            $render = $this->twig->render($template, $context);
            ob_flush();
        } catch (LoaderError|SyntaxError|RuntimeError $error) {
            $render = $this->twig->isDebug()
                ? "<div style='background: red; color: white'><strong>[DEBUG] Error in section \"{$section->getId()}\":</strong> {$error->getMessage()}</div>"
                : '';
        }

        return $render;
    }

    /**
     * Check whether the provided emailsection fulfills the conditions to be rendered
     * @param EmailSection $emailSection
     * @param array $context
     * @param CustomMail $mail
     *
     * @return bool Whether the conditions are met for rendering
     */
    protected function matchesCondition(EmailSection $emailSection, array $context, CustomMail $mail): bool
    {
        // early exit if no condition set
        if (!$emailSection->getCondition()) {
            return true;
        }
        foreach ($emailSection->getCondition() as $condition) {
            if ($this->conditionProcessor->process($condition, $context)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param CustomMail $mail
     * @return mixed
     */
    public function initConditionProcessor(CustomMail $mail)
    {
        $this->conditionProcessor = new ConditionProcessor();
        // register the condition functions for the current email object type
        $objectType = $mail->getObjectType();
        $functions = apply_filters('wrce_email_condition_functions', []);
        $functions = apply_filters("wrce_email_condition_functions/object_type=$objectType", $functions);
        $functions = apply_filters("wrce_email_condition_functions/recipient_type={$mail->getRecipientType()}", $functions);

        /** @var class-string<ConditionFunctionInterface>[] $functions */
        foreach ($functions as $functionClass) {
            if (!class_implements($functionClass, ConditionFunctionInterface::class)) {
                continue;
            }
            $instance = new $functionClass();

            $this->conditionProcessor->registerConditionFunction($instance);
        }
        return $objectType;
    }
}
