<?php

namespace Solspace\Freeform\Library\Integrations\Types\Elements;

use craft\base\Element;
use craft\base\ElementInterface;
use craft\models\FieldLayout;
use Solspace\Freeform\Attributes\Property\Implementations\FieldMapping\FieldMapItem;
use Solspace\Freeform\Attributes\Property\Implementations\FieldMapping\FieldMapping;
use Solspace\Freeform\Bundles\Form\ElementEdit\ElementEditBundle;
use Solspace\Freeform\Events\Integrations\BuildMappingContextEvent;
use Solspace\Freeform\Events\Integrations\ElementIntegrations\ProcessValueEvent;
use Solspace\Freeform\Form\Form;
use Solspace\Freeform\Library\Integrations\BaseIntegration;
use Solspace\Freeform\Library\Integrations\Rules\RulesBasedInterface;
use Solspace\Freeform\Library\Integrations\Rules\RulesTrait;
use yii\base\Event;

abstract class ElementIntegration extends BaseIntegration implements ElementIntegrationInterface, RulesBasedInterface
{
    use RulesTrait;

    public function onValidate(Form $form, Element $element): void {}

    public function onBeforeConnect(Form $form, Element $element): void {}

    public function onAfterConnect(Form $form, Element $element): void {}

    protected function getAssignedFormElement(Form $form): ?ElementInterface
    {
        $element = null;
        $elementId = ElementEditBundle::getElementId($form);
        if ($elementId) {
            $element = \Craft::$app->elements->getElementById($elementId);
        }

        return $element;
    }

    protected function getMappedValue(string $source, Form $form, ?FieldMapping $mapping = null): mixed
    {
        if (null === $mapping) {
            return null;
        }

        $event = new BuildMappingContextEvent($form, $this);
        Event::trigger($this, self::EVENT_BUILD_MAPPING_CONTEXT, $event);
        $mappingContext = $event->getContext();

        foreach ($mapping as $item) {
            if ($source === $item->getSource()) {
                return $item->extractValue($form, $mappingContext);
            }
        }

        return null;
    }

    protected function processMapping(ElementInterface $element, Form $form, ?FieldMapping $mapping = null): void
    {
        if (null === $mapping) {
            return;
        }

        $event = new BuildMappingContextEvent($form, $this);
        Event::trigger($this, self::EVENT_BUILD_MAPPING_CONTEXT, $event);
        $mappingContext = $event->getContext();

        $fieldLayout = $element->getFieldLayout();
        if (!$fieldLayout) {
            $fieldLayout = new FieldLayout();
        }

        $customFields = $fieldLayout->getCustomFields();
        foreach ($mapping as $item) {
            if (FieldMapItem::TYPE_RELATION === $item->getType() && empty($item->getValue())) {
                continue;
            }

            $craftField = null;
            foreach ($customFields as $field) {
                if ($field->handle === $item->getSource()) {
                    $craftField = $field;

                    break;
                }
            }

            $freeformField = null;
            if (FieldMapItem::TYPE_RELATION === $item->getType()) {
                $freeformField = $form->get($item->getValue());
            }

            $key = $item->getSource();
            $value = $item->extractValue($form, $mappingContext);

            $event = new ProcessValueEvent(
                $this,
                $form,
                $craftField,
                $freeformField,
                $key,
                $value
            );

            Event::trigger($this, self::EVENT_PROCESS_VALUE, $event);
            if (!$event->isValid) {
                continue;
            }

            if ($craftField) {
                $element->setFieldValue($craftField->handle, $event->getValue());
            } else {
                try {
                    $element->{$key} = $event->getValue();
                } catch (\TypeError) {
                    // Do nothing
                }
            }
        }
    }
}
