<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Serenity\MediaBundle\Trait;

use Serenity\CoreBundle\Notification\Command\NotificationCommand;
use Serenity\CoreBundle\Notification\Enum\PushLevel;
use Serenity\CoreBundle\Shared\Common\ConfigInterface;
use Serenity\MediaBundle\Doctrine\Repository\AttachmentRepository;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Uid\Uuid;
use Symfony\Contracts\Service\Attribute\Required;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;

trait ComponentMediaTrait
{
    private string $relativeDefaultLocalDirectory;
    private string $publicLocalDirectory;
    private AttachmentRepository $attachmentRepository;

    #[Required]
    public function setLocalDirectory(
        #[Autowire(param: 'relative_default_local_directory')]
        string $relativeDefaultLocalDirectory,
        #[Autowire(param: 'public_local_directory')]
        string $publicLocalDirectory,
    ): void {
        $this->relativeDefaultLocalDirectory = $relativeDefaultLocalDirectory;
        $this->publicLocalDirectory = $publicLocalDirectory;
    }

    #[Required]
    public function setAttachmentRepository(
        AttachmentRepository $attachmentRepository,
    ): void {
        $this->attachmentRepository = $attachmentRepository;
    }

    #[LiveProp(writable: false)]
    #[ExposeInTemplate]
    public array $uploadedFiles = [];

    #[LiveListener('uploadedFilesChanged')]
    public function changeUploadedFiles(#[LiveArg] array $addUploadedFiles = [], #[LiveArg] array $removeUploadedFiles = [], #[LiveArg] bool $refresh = false): void
    {
        foreach ($addUploadedFiles as $key => $uploadedFile) {
            $this->uploadedFiles[preg_replace('/^[^_]+_/', '', $key)] = $uploadedFile;
        }

        foreach ($removeUploadedFiles as $uploadedFileKey) {
            $this->dispatchBrowserEvent('preview:remove', ['id' => $uploadedFileKey]);
            unset($this->uploadedFiles[preg_replace('/^[^_]+_/', '', $uploadedFileKey)]);
        }

        if ($refresh) {
            $this->formValues = array_replace_recursive($this->formValues, $this->deserializeUploadedFiles($this->uploadedFiles));
        }
    }

    protected function deserializeUploadedFiles(array $flatArray): array
    {
        $nested = [];

        foreach ($flatArray as $flatKey => $value) {
            $keys = explode('_', $flatKey);
            $ref = &$nested;

            foreach ($keys as $key) {
                if (!isset($ref[$key]) || !\is_array($ref[$key])) {
                    $ref[$key] = [];
                }
                $ref = &$ref[$key];
            }

            if (\is_array($value) && isset($value['path'], $value['originalName'], $value['mimeType'])) {
                $value = new UploadedFile(
                    $this->publicLocalDirectory.'/'.$value['path'], // chemin réel du fichier dans /tmp
                    $value['originalName'],
                    $value['mimeType'],
                    null,
                    true  // $test = true : ce n’est pas un vrai upload HTTP (mais simulé)
                );
            }
            $ref = $value;
        }

        return $nested;
    }

    /** Utile pour persister manuellement si besoins les entités se touvant dans un json_document comme les Attachments ( Image, File, .. )   */
    protected function manualPersistAttachment(object $object, array $deserializeUploadedFiles): object
    {
        foreach ($this->findKeysLeadingToUploadedFile($deserializeUploadedFiles) as $property) {
            $accessor = PropertyAccess::createPropertyAccessor();
            $value = $accessor->getValue($object, $property);
            if ($object instanceof ConfigInterface) {
                $this->entityManager->persist($value);
                $this->entityManager->flush();
            }
        }

        return $object;
    }

    #[LiveAction]
    public function removeMedia(#[LiveArg] string $fieldName, #[LiveArg] ?string $id = null, #[LiveArg] bool $required = false): void
    {
        if (isset($this->uploadedFiles[preg_replace('/^[^_]+_/', '', $fieldName)])) {
            $this->emitSelf('uploadedFilesChanged', ['removeUploadedFiles' => [$fieldName]]);

            return;
        }
        if ($id) {
            if ($required) {
                $notification = new NotificationCommand(
                    message: 'L\'image est obligatoire, elle ne peut-être supprimée',
                    level: PushLevel::ERROR
                );
                $this->commandBus->dispatch($notification);

                return;
            }
            if ($file = $this->attachmentRepository->find($id)) {
                $this->attachmentRepository->delete($file);
                $this->resetForm();
            }
        }
    }

    #[LiveAction]
    public function removeCollectionMedia(#[LiveArg] array $identifiers): void
    {
        $removeUploadedFiles = [];
        foreach ($identifiers as $identifier) {
            if (Uuid::isValid($identifier)) {
                $object = $this->attachmentRepository->find($identifier);
                if ($object) {
                    $this->attachmentRepository->remove($object);
                }
            } else {
                $removeUploadedFiles[] = $identifier;
            }
        }
        $this->attachmentRepository->flush();
        if (!empty($removeUploadedFiles)) {
            $this->changeUploadedFiles(removeUploadedFiles: $removeUploadedFiles);
        }
    }

    private function findKeysLeadingToUploadedFile(array $data, array $parents = []): array
    {
        $results = [];

        foreach ($data as $key => $value) {
            $currentPath = [...$parents, $key];

            if ($value instanceof UploadedFile) {
                if (\count($currentPath) >= 2) {
                    $results[] = $currentPath[\count($currentPath) - 2];
                }
            } elseif (\is_array($value)) {
                $results = array_merge(
                    $results,
                    $this->findKeysLeadingToUploadedFile($value, $currentPath)
                );
            }
        }

        return array_unique($results);
    }
}
