<?php

namespace AutoAdmin\Bundle\Media\Form\Type\Media;

use AutoAdmin\Bundle\Admin\Form\Type\Component\AbstractHelperType;
use AutoAdmin\Bundle\Core\Helper\DoctrineHelper;
use AutoAdmin\Bundle\Media\Attributes\LinkedMedia;
use AutoAdmin\Bundle\Media\Library\LinkedMediaPropHelper;
use AutoAdmin\Bundle\Media\Library\MediaLibrary;
use AutoAdmin\Bundle\Media\Library\MediaTypeResolver;
use AutoAdmin\Bundle\Media\Library\PreviewGenerator;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccessor;

class LinkedMediaType extends AbstractHelperType
{
    public function __construct(
        protected MediaTypeResolver $mediaTypeResolver,
        protected MediaLibrary $library,
        protected PreviewGenerator $previewGenerator,
        protected LinkedMediaPropHelper $linkedMediaPropHelper,
    ) {
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        parent::configureOptions($resolver);

        $resolver->setDefault('media_types', []);
        $resolver->setDefault('empty_data', '');
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        parent::buildForm($builder, $options);

        $builder->add('media_type', HiddenType::class);
        $builder->add('media_pk', HiddenType::class);

        $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {

            $form = $event->getForm();
            $data = $form->getParent()?->getData();
            $formName = $form->getName();

            if ($data && $formName) {
                $pa = new PropertyAccessor();
                if (is_array($data)) {
                    try {
                        $entryType = $pa->getValue($form, 'parent.config.options[entry_type]');
                    } catch (\Exception $e) {
                        $entryType = null;
                    }

                    if ($entryType) {
                        // probably in a collection, skip collection to grab data object
                        $data = $pa->getValue($form, 'parent.parent.data');
                        $formName = $pa->getValue($form, 'parent.name');
                    }
                }

                $dataClass = DoctrineHelper::getClass($data);
                $propConfig = $this->linkedMediaPropHelper->getLinkedProp($dataClass, $formName);
                if ($propConfig === null) {
                    throw new \RuntimeException(
                        'Linked media property must declare its mappings using: "'.LinkedMedia::class.'"'
                    );
                }
                $mappingsCsv = implode(',', $propConfig->getMappings());
                $form->add('mappings', HiddenType::class, ['mapped' => false, 'data' => $mappingsCsv]);
            }
        });

        $builder->addModelTransformer(
            new CallbackTransformer(
                function ($linkAsString): array {
                    // transform the string back to an array
                    $parts = explode('#', $linkAsString);

                    return [
                        'media_type' => $parts[0] ?? null,
                        'media_pk' => $parts[1] ?? null,
                    ];
                },

                function ($linkAsArray): string {
                    // transform the array to a string

                    if (empty($linkAsArray['media_type']) || empty($linkAsArray['media_pk'])) {
                        return '';
                    }

                    return $linkAsArray['media_type'].'#'.$linkAsArray['media_pk'];
                },
            )
        );
    }

    public function buildView(FormView $view, FormInterface $form, array $options): void
    {
        parent::buildView($view, $form, $options);

        $this->addViewClass($view, 'aa-ft-linked-media');

        if ($form->has('mappings')) {
            $view->vars['mediaTypes'] = $form->get('mappings')?->getData();
        } else {
            $view->vars['mediaTypes'] = 'image,file';
        }
        if (
            ($type = $form->get('media_type')->getData())
            && ($id = $form->get('media_pk')->getData())
            && ($media = $this->library->get($type, $id))
        ) {
            $view->vars['mediaPreview'] = $this->previewGenerator->getPreview($type, $id);
            $view->vars['currentMedia'] = $media;
        } else {
            $isUnlinked = empty($media) && $type && $id;
            $view->vars['mediaPreview'] = $this->previewGenerator->getEmptyPreview($isUnlinked);
            $view->vars['currentMedia'] = null;
        }
    }

    public function getBlockPrefix(): string
    {
        return 'aa_linked_media';
    }
}
