<?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 CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductVariant;
use CpCreation\VitiCore\Product\Model\ProductVariantOption;
use CpCreation\VitiCore\Product\Repository\ProductVariantRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfonycasts\DynamicForms\DependentField;
use Symfonycasts\DynamicForms\DynamicFormBuilder;

class CartItemType extends AbstractType
{
    public function __construct(
        private ProductVariantRepository $productVariantRepository,
    ) {
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder = new DynamicFormBuilder($builder);
        /** @var CartItem $item */
        $item = $builder->getData();
        $product = $options['product'];
        $optionExpanded = (bool) $options['option_expanded'];
        if ($options['product']) {
            $builder->add('variant', EntityType::class, [
                'class' => ProductVariant::class,
                'placeholder' => 'shop.select_volume',
                'choices' => $this->productVariantRepository->getVariantsByProduct($product),
                'choice_loader' => null,
                'required' => false,
                'choice_label' => function (ProductVariant $variant) {
                    return $variant->getLabel();
                },
                'choice_value' => static function (?ProductVariant $variant): string {
                    return $variant?->getId()?->toString() ?? '';
                },
                'multiple' => false,
                'label' => false,
            ]
            );
        }

        $initialOptionVariant = $item?->getVariant() ?? $product?->getVariantByDefault();
        $initialOptions = $this->buildSortedOptions($initialOptionVariant?->getOptions()->toArray() ?? []);

        if ([] !== $initialOptions) {
            $builder->add('option', ChoiceType::class, [
                'mapped' => false,
                'required' => false,
                'placeholder' => $optionExpanded ? false : 'shop.select_option',
                'expanded' => $optionExpanded,
                'choices' => $initialOptions,
                'choice_label' => [$this, 'buildOptionLabel'],
                'choice_attr' => [$this, 'buildOptionAttributes'],
                'choice_value' => static fn (?ProductVariantOption $option): string => $option?->getId()?->toString() ?? '',
                'label' => false,
            ]);
        }

        $builder->addDependent('option', 'variant', function (DependentField $field, ?ProductVariant $variant) use ($optionExpanded) {
            $options = $this->buildSortedOptions($variant?->getOptions()->toArray() ?? []);

            if ([] === $options) {
                return;
            }

            $field->add(ChoiceType::class, [
                'mapped' => false,
                'required' => false,
                'placeholder' => $optionExpanded ? false : 'shop.select_option',
                'expanded' => $optionExpanded,
                'choices' => $options,
                'choice_label' => [$this, 'buildOptionLabel'],
                'choice_attr' => [$this, 'buildOptionAttributes'],
                'choice_value' => static fn (?ProductVariantOption $option): string => $option?->getId()?->toString() ?? '',
                'label' => false,
            ]);
        });

        $builder->add('quantity', IntegerType::class, [
            'required' => true,
            'label' => false,
            'attr' => $this->buildQuantityAttributes($item?->getVariant()),
        ]
        );
        $builder->addDependent('quantity', 'variant', function (DependentField $field, ?ProductVariant $variant) {
            if ($variant && $variant->isAvailable()) {
                $field->add(IntegerType::class, [
                    'required' => true,
                    'empty_data' => 1,
                    'label' => false,
                    'attr' => $this->buildQuantityAttributes($variant),
                ]
                );
            }
            if (!$variant) {
                $field->add(IntegerType::class, [
                    'required' => true,
                    'empty_data' => 1,
                    'label' => false,
                    'attr' => $this->buildQuantityAttributes(),
                ]
                );
            }
        });
    }

    /**
     * @param list<ProductVariantOption> $options
     *
     * @return list<ProductVariantOption>
     */
    private function buildSortedOptions(array $options): array
    {
        usort($options, static function (ProductVariantOption $left, ProductVariantOption $right): int {
            return ($left->getPosition() ?? PHP_INT_MAX) <=> ($right->getPosition() ?? PHP_INT_MAX);
        });

        return $options;
    }

    public function buildOptionLabel(ProductVariantOption $option): string
    {
        $label = (string) $option->getName();
        if ($option->getPrice() > 0) {
            $label = sprintf(
                '%s (+%s €%s)',
                $label,
                number_format($option->getPrice() / 100, 2, ',', ' '),
                $option->isFlatFee() ? ' forfait' : '/u'
            );
        }

        if (!$option->isAvailable()) {
            $label .= ' - épuisé';
        }

        return $label;
    }

    /**
     * @return array<string, bool|string>
     */
    public function buildOptionAttributes(ProductVariantOption $option): array
    {
        if ($option->isAvailable()) {
            return [];
        }

        return [
            'disabled' => true,
            'data-option-unavailable' => '1',
        ];
    }

    /**
     * @return array<string, int|string>
     */
    private function buildQuantityAttributes(?ProductVariant $variant = null): array
    {
        $lot = max(1, $variant?->getLot() ?? 1);
        $attributes = [
            'min' => $lot,
            'step' => $lot,
            'class' => 'w-16 bg-transparent',
        ];

        if (null !== $variant?->getStock()) {
            $attributes['max'] = $variant->getStock();
        }

        return $attributes;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(
            [
                'data_class' => CartItem::class,
                'product' => null,
                'option_expanded' => false,
            ]
        );
        $resolver->setAllowedTypes('product', [Product::class, 'null']);
        $resolver->setAllowedTypes('option_expanded', 'bool');
    }
}
