<?php

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Modal;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductDegressive;
use CpCreation\VitiCore\Product\Model\ProductPrice;
use CpCreation\VitiCore\Product\Model\ProductVariant;
use CpCreation\VitiCore\Product\Repository\ProductDegressiveRepository;
use CpCreation\VitiCore\Product\Repository\ProductPriceGroupRepository;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\ComponentToolsTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('modal:SimpleModalDegressivity', template: '@components/modal/SimpleModalDegressivity.html.twig')]
class SimpleModalDegressivity
{
    use ComponentToolsTrait;
    use DefaultActionTrait;

    public ?string $id = 'degressive-modal';
    public bool $open = false;
    public string $title = '';
    public string $variantLabel = '';

    /**
     * @var list<array{key: string, range_label: string, price_label: string, detail_label: string}>
     */
    public array $rules = [];

    public function __construct(
        private readonly ProductDegressiveRepository $productDegressiveRepository,
        private readonly ProductPriceGroupRepository $productPriceGroupRepository,
        private readonly RequestStack $requestStack,
        private readonly TranslatorInterface $translator,
    ) {
    }

    #[LiveListener('degressiveModal:open')]
    public function openDegressiveModal(
        #[LiveArg('productId')] Product $product,
        #[LiveArg('variantId')] ?ProductVariant $variant = null,
    ): void {
        if (!$variant instanceof ProductVariant || $variant->getProduct()?->getId() !== $product->getId()) {
            $variant = $product->getVariantByDefault();
        }

        $this->title = $this->translator->trans('shop.degressive_modal_title');
        $this->variantLabel = $variant instanceof ProductVariant ? $variant->getFullName() : '';
        $this->rules = [];

        if ($variant instanceof ProductVariant) {
            $groupId = $this->resolvePriceGroupId();
            $rules = $this->productDegressiveRepository->findAllApplicableByProductAndByGroup($product, $groupId);
            $basePrice = $this->resolveVariantFinalPrice($variant, $groupId);

            $variantRules = array_values(array_filter(
                $rules,
                static fn (ProductDegressive $rule): bool => $rule->getTypes()->contains($variant->getType()),
            ));

            usort($variantRules, static function (ProductDegressive $left, ProductDegressive $right): int {
                $byQty = ($left->getQtyOf() ?? 0) <=> ($right->getQtyOf() ?? 0);

                if (0 !== $byQty) {
                    return $byQty;
                }

                return ($left->getQtyAt() ?? 0) <=> ($right->getQtyAt() ?? 0);
            });

            $this->rules = array_values(array_filter(array_map(
                fn (ProductDegressive $rule): ?array => $this->buildDegressiveRuleData($rule, $basePrice),
                $variantRules,
            )));
        }

        $this->open = true;
    }

    #[LiveAction]
    public function close(): void
    {
        $this->open = false;
    }

    private function resolvePriceGroupId(): ?string
    {
        $groupId = GroupContext::$groupId;

        if (!\is_string($groupId) || '' === $groupId) {
            $groupId = $this->requestStack->getSession()?->get('groupPrice');
        }

        if (!\is_string($groupId) || '' === $groupId) {
            $groupId = $this->productPriceGroupRepository->getGroupByDefault()?->getId()?->toString();
        }

        return \is_string($groupId) && '' !== $groupId ? $groupId : null;
    }

    private function resolveVariantFinalPrice(ProductVariant $variant, ?string $groupId): ?int
    {
        $price = $this->resolveVariantPrice($variant, $groupId);

        return $price instanceof ProductPrice ? $price->getFinalPrice() : null;
    }

    private function resolveVariantPrice(ProductVariant $variant, ?string $groupId): ?ProductPrice
    {
        if (\is_string($groupId) && '' !== $groupId) {
            foreach ($variant->getPrices() as $price) {
                if ($price->getGroup()?->getId()?->toString() === $groupId) {
                    return $price;
                }
            }
        }

        return $variant->getVariantPriceByGroupAvailable();
    }

    /**
     * @return array{key: string, range_label: string, price_label: string, detail_label: string}|null
     */
    private function buildDegressiveRuleData(ProductDegressive $rule, ?int $basePrice): ?array
    {
        if (null === $basePrice || $basePrice <= 0) {
            return null;
        }

        $minQty = max(1, (int) $rule->getQtyOf() + 1);
        $maxQty = max($minQty, (int) ($rule->getQtyAt() ?? $minQty));
        $unitPrice = $this->computeDegressiveUnitPrice($rule, $basePrice, $minQty);

        return [
            'key' => $rule->getId()->toString(),
            'range_label' => $this->formatQtyRange($minQty, $maxQty),
            'price_label' => $this->formatCents($unitPrice).' €',
            'detail_label' => $this->formatDegressiveDetail($rule),
        ];
    }

    private function computeDegressiveUnitPrice(ProductDegressive $rule, int $basePrice, int $quantity): int
    {
        $lineBaseAmount = $basePrice * $quantity;
        $discountAmount = 0;

        if (null !== $rule->getBottlePriceTTC() && $rule->getBottlePriceTTC() > 0) {
            $discountAmount += $rule->getBottlePriceTTC() * $quantity;
        } elseif (null !== $rule->getPackagePriceTTC() && $rule->getPackagePriceTTC() > 0) {
            $discountAmount += $rule->getPackagePriceTTC();
        }

        if (null !== $rule->getPackagePercent() && $rule->getPackagePercent() > 0) {
            $discountAmount += $this->roundMoney($lineBaseAmount * $rule->getPackagePercent());
        }

        if (null !== $rule->getQtyOfBottleOffered() && $rule->getQtyOfBottleOffered() > 0) {
            $bundleSize = max(1, ((int) $rule->getQtyOf()) + 1);
            $offeredCount = (int) floor($quantity / $bundleSize) * (int) $rule->getQtyOfBottleOffered();
            $discountAmount += $basePrice * $offeredCount;
        }

        $finalAmount = max(0, $lineBaseAmount - min($discountAmount, $lineBaseAmount));

        return $this->roundMoney($finalAmount / max(1, $quantity));
    }

    private function formatQtyRange(int $minQty, int $maxQty): string
    {
        if ($maxQty >= 100000) {
            return $this->translator->trans('shop.degressive_from_qty', ['%qty%' => $minQty]);
        }

        return $this->translator->trans('shop.degressive_range_qty', [
            '%from%' => $minQty,
            '%to%' => $maxQty,
        ]);
    }

    private function formatDegressiveDetail(ProductDegressive $rule): string
    {
        $labels = [];

        if (null !== $rule->getBottlePriceTTC() && $rule->getBottlePriceTTC() > 0) {
            $labels[] = $this->translator->trans('shop.degressive_detail_per_bottle', [
                '%amount%' => $this->formatCents($rule->getBottlePriceTTC()),
            ]);
        } elseif (null !== $rule->getPackagePriceTTC() && $rule->getPackagePriceTTC() > 0) {
            $labels[] = $this->translator->trans('shop.degressive_detail_on_bundle', [
                '%amount%' => $this->formatCents($rule->getPackagePriceTTC()),
            ]);
        }

        if (null !== $rule->getPackagePercent() && $rule->getPackagePercent() > 0) {
            $labels[] = sprintf('-%s %%', number_format($rule->getPackagePercent() * 100, 0, ',', ' '));
        }

        if (null !== $rule->getQtyOfBottleOffered() && $rule->getQtyOfBottleOffered() > 0) {
            $labels[] = $this->translator->trans(
                1 === $rule->getQtyOfBottleOffered()
                    ? 'shop.degressive_offered_bottle'
                    : 'shop.degressive_offered_bottles',
                ['%count%' => $rule->getQtyOfBottleOffered()],
            );
        }

        return implode(' + ', $labels);
    }

    private function formatCents(int $amount): string
    {
        return number_format($amount / 100, 2, ',', ' ');
    }

    private function roundMoney(float $value): int
    {
        return (int) round($value, 0, \PHP_ROUND_HALF_UP);
    }
}
