<?php

/*
 * This file is part of the VITICORE2 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 CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Order;

use A2lix\TranslationFormBundle\Form\Type\TranslatedEntityType;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Order\CgvType;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\PaymentMethod;
use CpCreation\VitiCore\Cart\Repository\PaymentMethodRepository;
use CpCreation\VitiCore\Shop\Model\DeliveryMethod;
use CpCreation\VitiCore\Shop\Repository\DeliveryMethodRepository;
use CpCreation\VitiCore\Shop\Repository\ShippingConfigRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;

class CheckoutType extends AbstractType
{
    public function __construct(
        private readonly CheckoutDeliveryMethodViewBuilder $checkoutDeliveryMethodViewBuilder,
        private readonly ShippingConfigRepository $shippingConfigRepository,
        private readonly TranslatorInterface $translator,
    ) {
    }

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

        if ($form->has('paymentMethod') && isset($view['paymentMethod'])) {
            $paymentMethodView = $view['paymentMethod'];
            $childrenByValue = [];
            $choices = [];

            foreach ($paymentMethodView as $childView) {
                $childrenByValue[(string) $childView->vars['value']] = $childView;
            }

            foreach (($paymentMethodView->vars['choices'] ?? []) as $choiceView) {
                if (!$choiceView instanceof ChoiceView) {
                    continue;
                }

                if (!$choiceView->data instanceof PaymentMethod) {
                    continue;
                }

                $childView = $childrenByValue[(string) $choiceView->value] ?? null;

                if (null === $childView) {
                    continue;
                }

                $configuration = $choiceView->data->getConfiguration();
                $icon = $choiceView->data->getImage();
                $iconPath = $icon?->getPath();
                $childView->vars['payment_method'] = $choiceView->data;
                $childView->vars['payment_method_meta'] = [
                    'name' => (string) ($configuration['name'] ?? $choiceView->data->getMethod() ?? ''),
                    'subtitle' => (string) ($configuration['subtitle'] ?? ''),
                    'payment_online' => $choiceView->data->isPaymentOnline(),
                    'icon_path' => $iconPath,
                    'icon_type' => $icon?->getMimeType(),
                    'icon_alt' => $icon?->getTitle() ?: (string) ($configuration['name'] ?? $choiceView->data->getMethod() ?? ''),
                ];

                $choices[] = [
                    'child' => $childView,
                    'method' => $choiceView->data,
                    'name' => (string) ($configuration['name'] ?? $choiceView->data->getMethod() ?? ''),
                    'subtitle' => (string) ($configuration['subtitle'] ?? ''),
                    'payment_online' => $choiceView->data->isPaymentOnline(),
                    'icon_path' => $iconPath,
                    'icon_type' => $icon?->getMimeType(),
                    'icon_alt' => $icon?->getTitle() ?: (string) ($configuration['name'] ?? $choiceView->data->getMethod() ?? ''),
                ];
            }

            $paymentMethodView->vars['payment_method_choices'] = $choices;
        }

        if (!$form->has('deliveryMethod') || !isset($view['deliveryMethod'])) {
            return;
        }

        $deliveryMethodView = $view['deliveryMethod'];
        $deliveryMethodView->vars['standard_shipping_text'] = $this->resolveStandardShippingText();
        $cart = $form->getData();

        if (!$cart instanceof Cart) {
            $deliveryMethodView->vars['standard_choice'] = null;
            $deliveryMethodView->vars['grouped_methods'] = [];

            return;
        }

        $selectedMethod = $form->get('deliveryMethod')->getData();
        $deliveryMethodView->vars['standard_choice'] = $this->checkoutDeliveryMethodViewBuilder->buildStandardChoice(
            $cart,
            null === $selectedMethod,
        );
        $childrenByValue = [];
        $choiceViewsByMethodId = [];
        $methods = [];

        foreach ($deliveryMethodView as $childView) {
            $childrenByValue[(string) $childView->vars['value']] = $childView;
        }

        foreach (($deliveryMethodView->vars['choices'] ?? []) as $choiceView) {
            if (!$choiceView instanceof ChoiceView) {
                continue;
            }

            if (!$choiceView->data instanceof DeliveryMethod) {
                continue;
            }

            $choiceViewsByMethodId[$choiceView->data->getId()->toString()] = $choiceView;
            $methods[] = $choiceView->data;
        }

        $groupedMethods = $this->checkoutDeliveryMethodViewBuilder->build($cart, $methods, $selectedMethod);

        if ([] === $groupedMethods && [] !== $methods) {
            $groupedMethods = $this->buildFallbackGroups($methods, $selectedMethod, $cart);
        }

        $groups = [];

        foreach ($groupedMethods as $group) {
            $choices = [];

            foreach ($group['choices'] as $choiceMeta) {
                $method = $choiceMeta['method'];
                $methodId = $method->getId()->toString();
                $choiceView = $choiceViewsByMethodId[$methodId] ?? null;
                $childView = $choiceView instanceof ChoiceView
                    ? ($childrenByValue[(string) $choiceView->value] ?? null)
                    : null;

                if (null === $childView) {
                    continue;
                }

                $childView->vars['delivery_method'] = $method;
                $childView->vars['delivery_method_meta'] = $choiceMeta;

                $choices[] = array_merge($choiceMeta, [
                    'child' => $childView,
                ]);
            }

            if ([] === $choices) {
                continue;
            }

            $group['choices'] = $choices;
            $group['single'] = 1 === \count($choices);
            $groups[] = $group;
        }

        $deliveryMethodView->vars['grouped_methods'] = $groups;
    }

    private function resolveStandardShippingText(): ?string
    {
        $shippingConfig = $this->shippingConfigRepository->findOne();

        if (null === $shippingConfig) {
            return null;
        }

        $shippingText = $shippingConfig
            ->translate($shippingConfig->getCurrentLocale())
            ->getShippingText();

        $shippingText = null !== $shippingText ? trim($shippingText) : null;

        return '' !== (string) $shippingText ? $shippingText : null;
    }

    /**
     * @param list<DeliveryMethod> $methods
     *
     * @return list<array{
     *     id: string,
     *     name: ?string,
     *     min_view: bool,
     *     open: bool,
     *     single: bool,
     *     choices: list<array{
     *         method: DeliveryMethod,
     *         amount: int,
     *         price_label: string,
     *         available: bool
     *     }>
     * }>
     */
    private function buildFallbackGroups(array $methods, ?DeliveryMethod $selectedMethod, Cart $cart): array
    {
        $selectedId = $selectedMethod?->getId()->toString();
        $groups = [];

        foreach ($methods as $method) {
            $category = $method->getCategory();

            if (null !== $category) {
                $groupKey = $category->getId()->toString();
                $groupName = $category->getName();
                $minView = $category->isMinView();
            } else {
                $groupKey = 'method_'.$method->getId()->toString();
                $groupName = null;
                $minView = false;
            }

            if (!isset($groups[$groupKey])) {
                $groups[$groupKey] = [
                    'id' => $groupKey,
                    'name' => $groupName,
                    'min_view' => $minView,
                    'choices' => [],
                    'open' => false,
                    'single' => false,
                ];
            }

            $groups[$groupKey]['choices'][] = [
                'method' => $method,
                'amount' => 0,
                'price_label' => $this->resolveFallbackPriceLabel($method, $cart),
                'available' => true,
            ];

            if (null !== $selectedId && $selectedId === $method->getId()->toString()) {
                $groups[$groupKey]['open'] = true;
            }
        }

        foreach ($groups as &$group) {
            $group['single'] = 1 === \count($group['choices']);
        }
        unset($group);

        return array_values($groups);
    }

    private function resolveFallbackPriceLabel(DeliveryMethod $method, Cart $cart): string
    {
        if ($method->isDepot()) {
            return $this->translator->trans('checkout_design.free');
        }

        if ($method->isEnabledShippingPrice()) {
            return $this->translator->trans('checkout_design.to_define');
        }

        if (null !== $method->getPackagePrice()) {
            if ($method->getPackagePrice() <= 0) {
                return $this->translator->trans('checkout_design.free');
            }

            return \sprintf('%s €', number_format($method->getPackagePrice() / 100, 2, ',', ' '));
        }

        if (null !== $cart->getCartShipping()) {
            $amount = $cart->getCartShipping()->amountTTC;

            if ($amount <= 0) {
                return $this->translator->trans('checkout_design.free');
            }

            return \sprintf('%s €', number_format($amount / 100, 2, ',', ' '));
        }

        return $this->translator->trans('checkout_design.to_define');
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add(
                'deliveryAddress', DeliveryAddressType::class, [
                    'label' => false,
                    'required' => true,
                ]
            )
            ->add(
                'invoiceAddress', InvoiceAddressType::class, [
                    'label' => false,
                    'required' => true,
                ]
            )
            ->add(
                'deliveryMethod', TranslatedEntityType::class, [
                    'class' => DeliveryMethod::class,
                    'label' => false,
                    'placeholder' => false,
                    'multiple' => false,
                    'expanded' => true,
                    'required' => false,
                    'query_builder' => function (DeliveryMethodRepository $er) {
                        return $er->findPublishQueryBuilder(GroupContext::$groupId);
                    },
                    'translation_property' => 'name',
                    'choice_attr' => static function (): array {
                        return [
                            'data-action' => 'change->live#action',
                            'data-live-action-param' => 'debounce(300)|saveDraft',
                        ];
                    },
                ]
            )
            ->add('paymentMethod', EntityType::class, [
                'class' => PaymentMethod::class,
                'query_builder' => static function (PaymentMethodRepository $repo) {
                    return $repo->findByPositionOrderQuery();
                },
                'expanded' => true,
                'label' => false,
                'required' => true,
                'choice_label' => static function (PaymentMethod $method): string {
                    $config = $method->getConfiguration();

                    return (string) ($config['name'] ?? '');
                },
            ])
            ->add(
                'comment', TextareaType::class, [
                    'label' => 'shop.form.comment',
                    'attr' => [
                        'rows' => '5',
                        'placeholder' => 'checkout_design.note_placeholder',
                    ],
                    'required' => false,
                ]
            )
            ->add('cgv', CgvType::class, [
                'label' => false,
                'required' => true,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(
            [
                'data_class' => Cart::class,
                'validation_groups' => static function (FormInterface $form): array {
                    $cart = $form->getData();
                    $groups = ['Default', 'delivery_address'];

                    if ($cart instanceof Cart && $cart->getInvoiceAddress()?->isDifferentAddress()) {
                        $groups[] = 'invoice_address';
                    }

                    return $groups;
                },
            ]
        );
    }
}
