<?php

declare(strict_types=1);

/*
 * 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\Command;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Tracking\MatomoEcommerceTracker;
use CpCreation\VitiCore\Bridge\Symfony\Bundle\Tracking\MetaConversionsApiTracker;
use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Repository\CartRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
    name: 'viti-core:cart:simulate-state',
    description: 'Simule un etat metier de panier pour tester abandoned/reactivated et les trackers associes.'
)]
final class SimulateCartStateCommand extends Command
{
    private const string STATE_ABANDONED = 'abandoned';
    private const string STATE_REACTIVATED = 'reactivated';

    public function __construct(
        private readonly CartRepository $cartRepository,
        private readonly MatomoEcommerceTracker $matomoEcommerceTracker,
        private readonly MetaConversionsApiTracker $metaConversionsApiTracker,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addArgument('cart', InputArgument::REQUIRED, 'UUID ou token du panier.')
            ->addArgument('state', InputArgument::REQUIRED, 'Etat a simuler: abandoned ou reactivated.')
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Affiche ce qui serait fait sans ecrire en base ni envoyer de tracking.')
            ->addOption('no-tracking', null, InputOption::VALUE_NONE, 'Modifie uniquement l etat metier sans envoyer Matomo/Meta.');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $identifier = $this->normalizeString($input->getArgument('cart'));
        $state = $this->normalizeString($input->getArgument('state'));
        $dryRun = (bool) $input->getOption('dry-run');
        $withTracking = !(bool) $input->getOption('no-tracking') && !$dryRun;

        if (null === $identifier) {
            $io->error('Le panier est obligatoire.');

            return Command::INVALID;
        }

        if (!\in_array($state, [self::STATE_ABANDONED, self::STATE_REACTIVATED], true)) {
            $io->error('Etat invalide. Valeurs acceptees: abandoned, reactivated.');

            return Command::INVALID;
        }

        $cart = $this->findCartByIdentifier($identifier);
        if (!$cart instanceof Cart) {
            $io->error(sprintf('Aucun panier trouve pour "%s".', $identifier));

            return Command::FAILURE;
        }

        $io->title('Simulation etat panier');
        $io->definitionList(
            ['Panier' => $this->describeCart($cart)],
            ['Etat actuel' => $cart->getCheckoutStatus()],
            ['Etat cible' => $state],
            ['Dry run' => $dryRun ? 'oui' : 'non'],
            ['Tracking' => $withTracking ? 'oui' : 'non']
        );

        return match ($state) {
            self::STATE_ABANDONED => $this->simulateAbandoned($cart, $io, $dryRun, $withTracking),
            self::STATE_REACTIVATED => $this->simulateReactivated($cart, $io, $dryRun, $withTracking),
        };
    }

    private function simulateAbandoned(Cart $cart, SymfonyStyle $io, bool $dryRun, bool $withTracking): int
    {
        if ($cart->isCheckoutPayed() || $cart->isCheckoutOrdered()) {
            $io->error('Ce panier est deja paye ou commande offline; il ne peut pas devenir abandonne.');

            return Command::FAILURE;
        }

        if ($cart->isAbandoned()) {
            $io->warning('Le panier est deja marque abandonne.');

            return Command::SUCCESS;
        }

        if ($dryRun) {
            $io->success('Dry-run: le panier serait marque abandonne.');

            return Command::SUCCESS;
        }

        $cart->markAbandoned(new \DateTimeImmutable());
        $this->cartRepository->save($cart);

        if ($withTracking) {
            $this->matomoEcommerceTracker->trackAbandonedCart($cart);
            $this->metaConversionsApiTracker->trackAbandonedCart($cart);
        }

        $io->success('Panier marque abandonne.');

        return Command::SUCCESS;
    }

    private function simulateReactivated(Cart $cart, SymfonyStyle $io, bool $dryRun, bool $withTracking): int
    {
        if ($cart->isCheckoutPayed() || $cart->isCheckoutOrdered()) {
            $io->error('Ce panier est deja paye ou commande offline; il ne peut pas etre reactive comme panier en cours.');

            return Command::FAILURE;
        }

        if (!$cart->isAbandoned()) {
            $io->warning('Le panier n est pas marque abandonne; aucune reactivation metier a simuler.');

            return Command::SUCCESS;
        }

        if ($dryRun) {
            $io->success('Dry-run: abandonedAt serait vide et lastActivityAt serait rafraichi.');

            return Command::SUCCESS;
        }

        $cart->touchActivity(new \DateTimeImmutable());
        $this->cartRepository->save($cart);

        if ($withTracking) {
            $this->matomoEcommerceTracker->trackReactivatedCart($cart);
            $this->metaConversionsApiTracker->trackReactivatedCart($cart);
        }

        $io->success('Panier reactive.');

        return Command::SUCCESS;
    }

    private function findCartByIdentifier(string $identifier): ?Cart
    {
        $cart = $this->cartRepository->find($identifier);

        if ($cart instanceof Cart) {
            return $cart;
        }

        $cart = $this->cartRepository->findOneBy(['token' => mb_strtoupper($identifier)]);

        return $cart instanceof Cart ? $cart : null;
    }

    private function normalizeString(mixed $value): ?string
    {
        if (!\is_scalar($value) && !$value instanceof \Stringable) {
            return null;
        }

        $value = mb_trim((string) $value);

        return '' !== $value ? $value : null;
    }

    private function describeCart(Cart $cart): string
    {
        return sprintf(
            '%s (%s)',
            $cart->getToken() ?: 'panier-sans-token',
            $cart->getId()->toString()
        );
    }
}
