<?php

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Admin;

use CpCreation\VitiCore\Shop\Model\ShopConfig;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class TrackingConfigType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('googleTagManagerId', TextType::class, [
                'required' => false,
                'label' => 'Google Tag Manager',
                'help' => 'Identifiant GTM, par exemple GTM-XXXXXXX.',
            ])
            ->add('googleAnalyticsIds', TextareaType::class, [
                'required' => false,
                'label' => 'IDs Google Analytics / Google Ads',
                'help' => 'Un identifiant par ligne. Exemple : G-XXXXXXX ou AW-XXXXXXX. Pour Google Ads, ajoutez ici l’ID AW puis renseignez les labels de conversion ci-dessous.',
                'attr' => [
                    'rows' => 4,
                    'placeholder' => "G-XXXXXXXXXX\nAW-XXXXXXXXXX",
                ],
            ])
            ->add('googleAdsConversionLabels', TextareaType::class, [
                'required' => false,
                'label' => 'Conversions Google Ads',
                'help' => 'Une conversion par ligne au format "event | AW-XXXXXXX/label". Exemples d’event : purchase, begin_checkout, generate_lead, cart_cleared.',
                'attr' => [
                    'rows' => 6,
                    'class' => 'font-monospace',
                    'placeholder' => "purchase | AW-XXXXXXXXXX/AbCdEfGh\nbegin_checkout | AW-XXXXXXXXXX/IjKlMnOp\ngenerate_lead | AW-XXXXXXXXXX/QrStUvWx\ncart_cleared | AW-XXXXXXXXXX/Yz123456",
                    'spellcheck' => 'false',
                ],
            ])
            ->add('facebookPixelIds', TextareaType::class, [
                'required' => false,
                'label' => 'IDs Meta Pixel (Facebook / Instagram)',
                'help' => 'Un identifiant de pixel/dataset par ligne. Utilisé côté navigateur pour PageView quand le Pixel Meta est activé.',
                'attr' => [
                    'rows' => 4,
                    'placeholder' => "123456789012345\n987654321098765",
                ],
            ])
            ->add('metaConversionsPixelConfigs', TextareaType::class, [
                'required' => false,
                'label' => 'Meta Conversions API serveur',
                'help' => 'Un pixel/dataset par ligne au format "pixel_id | access_token". Utilisé pour envoyer les événements serveur Facebook / Instagram.',
                'attr' => [
                    'rows' => 6,
                    'class' => 'font-monospace',
                    'autocomplete' => 'off',
                    'placeholder' => "123456789012345 | EAAB...\n987654321098765 | EAAB...",
                    'spellcheck' => 'false',
                ],
            ])
            ->add('metaTestEventCode', TextType::class, [
                'required' => false,
                'label' => 'Code de test Meta',
                'help' => 'Optionnel. À renseigner pendant les tests Events Manager Meta, puis à vider en production.',
            ])
            ->add('enableMetaBrowserEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer Meta Pixel navigateur',
                'help' => 'Charge le Pixel Meta côté navigateur pour PageView et l’identification navigateur (_fbp/_fbc).',
            ])
            ->add('enableMetaConversionsApiTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer Meta Conversions API serveur',
                'help' => 'Envoie les événements e-commerce côté serveur à Meta : panier, checkout, paiement, achat, abandon et réactivation.',
            ])
            ->add('matomoUrl', UrlType::class, [
                'required' => false,
                'label' => 'URL publique Matomo',
                'help' => 'URL utilisée par le navigateur pour charger matomo.js, par exemple https://analytics.example.com/.',
                'default_protocol' => 'https',
                'attr' => [
                    'placeholder' => 'https://analytics.example.com/',
                ],
            ])
            ->add('matomoServerUrl', UrlType::class, [
                'required' => false,
                'label' => 'URL serveur Matomo',
                'help' => 'URL privée utilisée uniquement par le serveur pour envoyer les commandes. Exemple local Docker : http://matomo/. Si vide, l’URL publique est utilisée.',
                'default_protocol' => 'http',
                'attr' => [
                    'placeholder' => 'http://matomo/',
                ],
            ])
            ->add('matomoSiteId', IntegerType::class, [
                'required' => false,
                'label' => 'ID du site Matomo',
                'help' => 'Identifiant numérique du site dans Matomo.',
                'attr' => [
                    'min' => 1,
                ],
            ])
            ->add('trackingHeadScripts', TextareaType::class, [
                'required' => false,
                'label' => 'Snippets personnalisés dans le <head>',
                'help' => 'Pour LinkedIn Insight Tag, TikTok Pixel, Pinterest Tag ou tout autre script à injecter dans le head.',
                'attr' => [
                    'rows' => 10,
                    'class' => 'font-monospace',
                ],
            ])
            ->add('trackingBodyScripts', TextareaType::class, [
                'required' => false,
                'label' => 'Snippets personnalisés au début du <body>',
                'help' => 'Pour les balises noscript ou les scripts qui doivent être injectés juste après l’ouverture du body.',
                'attr' => [
                    'rows' => 10,
                    'class' => 'font-monospace',
                ],
            ])
            ->add('enableCheckoutEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer l’événement `begin_checkout`',
                'help' => 'Nom exact envoyé à Google Analytics : begin_checkout. Déclenché automatiquement sur la page checkout.',
            ])
            ->add('enablePaymentSuccessEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer l’événement `purchase`',
                'help' => 'Nom exact envoyé à Google Analytics : purchase. Déclenché automatiquement sur la confirmation de paiement avec les données dynamiques de commande.',
            ])
            ->add('enablePaymentErrorEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer l’événement `payment_failed`',
                'help' => 'Nom exact envoyé à Google Analytics : payment_failed. Déclenché automatiquement lors d’un retour de paiement refusé ou en erreur.',
            ])
            ->add('enablePaymentCanceledEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer l’événement `payment_canceled`',
                'help' => 'Nom exact envoyé à Google Analytics : payment_canceled. Déclenché automatiquement quand le client annule le paiement.',
            ])
            ->add('enableContactPageEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer l’événement `view_contact_page`',
                'help' => 'Nom exact envoyé à Google Analytics : view_contact_page. Déclenché automatiquement sur la page contact.',
            ])
            ->add('enableContactSuccessEventTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer l’événement `generate_lead`',
                'help' => 'Nom exact envoyé à Google Analytics : generate_lead. Déclenché automatiquement après l’envoi réussi du formulaire de contact.',
            ])
            ->add('enableMatomoEcommerceTracking', CheckboxType::class, [
                'required' => false,
                'label' => 'Activer les événements e-commerce serveur Matomo',
                'help' => 'Envoie à Matomo les événements panier, checkout, achat, abandon et réactivation avec le visitor ID Matomo quand il est disponible.',
            ]);

        $builder->get('googleAnalyticsIds')->addModelTransformer(new CallbackTransformer(
            static fn (?array $ids): string => implode("\n", $ids ?? []),
            static fn (?string $ids): array => self::parseTrackerTextarea($ids),
        ));

        $builder->get('googleAdsConversionLabels')->addModelTransformer(new CallbackTransformer(
            static fn (?array $labels): string => self::formatGoogleAdsConversionLabels($labels),
            static fn (?string $labels): array => self::parseGoogleAdsConversionLabels($labels),
        ));

        $builder->get('facebookPixelIds')->addModelTransformer(new CallbackTransformer(
            static fn (?array $ids): string => implode("\n", $ids ?? []),
            static fn (?string $ids): array => self::parseTrackerTextarea($ids),
        ));

        $builder->get('metaConversionsPixelConfigs')->addModelTransformer(new CallbackTransformer(
            static fn (?array $configs): string => self::formatMetaConversionsPixelConfigs($configs),
            static fn (?string $configs): array => self::parseMetaConversionsPixelConfigs($configs),
        ));
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => ShopConfig::class,
            'translation_domain' => 'form',
        ]);
    }

    /**
     * @return list<string>
     */
    private static function parseTrackerTextarea(?string $value): array
    {
        if (null === $value) {
            return [];
        }

        $trackers = preg_split('/\R+/', $value) ?: [];
        $trackers = array_map(
            static fn (string $tracker): string => mb_strtoupper(mb_trim($tracker)),
            $trackers,
        );
        $trackers = array_values(array_filter($trackers, static fn (string $tracker): bool => '' !== $tracker));

        return array_values(array_unique($trackers));
    }

    /**
     * @param array<int|string, mixed>|null $labels
     */
    private static function formatGoogleAdsConversionLabels(?array $labels): string
    {
        if (null === $labels) {
            return '';
        }

        $lines = [];

        foreach ($labels as $eventName => $sendTo) {
            if (\is_array($sendTo)) {
                $eventName = $sendTo['event'] ?? $sendTo['eventName'] ?? $sendTo['event_name'] ?? $eventName;
                $sendTo = $sendTo['sendTo'] ?? $sendTo['send_to'] ?? $sendTo['conversion'] ?? null;
            }

            if (!\is_scalar($eventName) || !\is_scalar($sendTo)) {
                continue;
            }

            $eventName = mb_trim((string) $eventName);
            $sendTo = mb_trim((string) $sendTo);

            if ('' === $eventName || '' === $sendTo) {
                continue;
            }

            $lines[] = sprintf('%s | %s', $eventName, $sendTo);
        }

        return implode("\n", $lines);
    }

    /**
     * @return array<string, string>
     */
    private static function parseGoogleAdsConversionLabels(?string $value): array
    {
        if (null === $value || '' === mb_trim($value)) {
            return [];
        }

        $labels = [];
        $lines = preg_split('/\R+/', $value) ?: [];

        foreach ($lines as $line) {
            $line = mb_trim($line);
            if ('' === $line) {
                continue;
            }

            $parts = preg_split('/\s*\|\s*/', $line, 2) ?: [];
            if (2 !== \count($parts)) {
                throw new TransformationFailedException(sprintf('Format invalide pour "%s". Format attendu : event | AW-XXXX/label.', $line));
            }

            $eventName = mb_trim($parts[0]);
            $sendTo = mb_trim($parts[1]);

            if ('' === $eventName || '' === $sendTo) {
                throw new TransformationFailedException(sprintf('Format invalide pour "%s". Format attendu : event | AW-XXXX/label.', $line));
            }

            if (!preg_match('/^AW-[A-Z0-9_-]+\/[A-Z0-9_-]+$/i', $sendTo)) {
                throw new TransformationFailedException(sprintf('La conversion "%s" doit être au format AW-XXXX/label.', $sendTo));
            }

            $labels[$eventName] = $sendTo;
        }

        return $labels;
    }

    /**
     * @param array<int|string, mixed>|null $configs
     */
    private static function formatMetaConversionsPixelConfigs(?array $configs): string
    {
        if (null === $configs) {
            return '';
        }

        $lines = [];

        foreach ($configs as $config) {
            if (!\is_array($config)) {
                continue;
            }

            $pixelId = $config['pixelId'] ?? $config['pixel_id'] ?? null;
            $accessToken = $config['accessToken'] ?? $config['access_token'] ?? null;

            if (!\is_scalar($pixelId) || !\is_scalar($accessToken)) {
                continue;
            }

            $pixelId = mb_trim((string) $pixelId);
            $accessToken = mb_trim((string) $accessToken);

            if ('' === $pixelId || '' === $accessToken) {
                continue;
            }

            $lines[] = sprintf('%s | %s', $pixelId, $accessToken);
        }

        return implode("\n", $lines);
    }

    /**
     * @return list<array{pixelId: string, accessToken: string}>
     */
    private static function parseMetaConversionsPixelConfigs(?string $value): array
    {
        if (null === $value || '' === mb_trim($value)) {
            return [];
        }

        $configs = [];
        $seenPixelIds = [];
        $lines = preg_split('/\R+/', $value) ?: [];

        foreach ($lines as $lineNumber => $line) {
            $line = mb_trim($line);

            if ('' === $line) {
                continue;
            }

            $parts = preg_split('/\s*[|;]\s*/', $line, 2) ?: [];

            if (2 !== \count($parts)) {
                $parts = preg_split('/\s+/', $line, 2) ?: [];
            }

            if (2 !== \count($parts)) {
                throw new TransformationFailedException(sprintf(
                    'La ligne %d doit contenir un pixel ID et un token d’accès séparés par "|".',
                    $lineNumber + 1,
                ));
            }

            [$pixelId, $accessToken] = array_map(
                static fn (string $part): string => mb_trim($part),
                $parts,
            );

            if ('' === $pixelId || '' === $accessToken) {
                throw new TransformationFailedException(sprintf(
                    'La ligne %d doit contenir un pixel ID et un token d’accès non vides.',
                    $lineNumber + 1,
                ));
            }

            if (\in_array($pixelId, $seenPixelIds, true)) {
                continue;
            }

            $seenPixelIds[] = $pixelId;
            $configs[] = [
                'pixelId' => $pixelId,
                'accessToken' => $accessToken,
            ];
        }

        return $configs;
    }
}
