<?php

namespace App\Command;

use App\Entity\BilletBuyer;
use App\Entity\User;
use App\Service\AuditService;
use App\Service\BilletOrderService;
use App\Service\MailerService;
use App\Service\StripeService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
    name: 'app:stripe:sync',
    description: 'Sync Stripe account status for all organizers and reconcile pending orders',
)]
class StripeSyncCommand extends Command
{
    public function __construct(
        private EntityManagerInterface $em,
        private StripeService $stripeService,
        private BilletOrderService $billetOrderService,
        private MailerService $mailerService,
        private AuditService $audit,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        $hasErrors = $this->syncAccounts($io);
        $hasErrors = $this->syncPendingOrders($io) || $hasErrors;

        return $hasErrors ? Command::FAILURE : Command::SUCCESS;
    }

    private function syncAccounts(SymfonyStyle $io): bool
    {
        $organizers = array_filter(
            $this->em->getRepository(User::class)->findAll(),
            fn (User $u) => \in_array('ROLE_ORGANIZER', $u->getRoles(), true) && null !== $u->getStripeAccountId(),
        );

        if (0 === \count($organizers)) {
            $io->info('No organizers with Stripe accounts found.');

            return false;
        }

        $io->info(sprintf('Syncing %d organizer(s)...', \count($organizers)));

        $synced = 0;
        $errors = 0;

        foreach ($organizers as $user) {
            try {
                $status = $this->stripeService->retrieveAccountStatus($user->getStripeAccountId());

                $chargesBefore = $user->isStripeChargesEnabled();
                $payoutsBefore = $user->isStripePayoutsEnabled();

                $user->setStripeChargesEnabled($status['charges_enabled']);
                $user->setStripePayoutsEnabled($status['payouts_enabled']);

                $changed = $chargesBefore !== $user->isStripeChargesEnabled()
                    || $payoutsBefore !== $user->isStripePayoutsEnabled();

                $status = $changed ? '<fg=yellow>UPDATED</>' : '<fg=green>OK</>';
                $io->text(sprintf(
                    '  [%s] %s (%s) — charges: %s, payouts: %s',
                    $status,
                    $user->getCompanyName() ?? $user->getEmail(),
                    $user->getStripeAccountId(),
                    $user->isStripeChargesEnabled() ? 'yes' : 'no',
                    $user->isStripePayoutsEnabled() ? 'yes' : 'no',
                ));

                ++$synced;
            } catch (\Throwable $e) {
                $io->text(sprintf(
                    '  [<fg=red>ERROR</>] %s (%s) — %s',
                    $user->getCompanyName() ?? $user->getEmail(),
                    $user->getStripeAccountId(),
                    $e->getMessage(),
                ));
                ++$errors;
            }
        }

        $this->em->flush();

        $io->success(sprintf('Accounts: %d synced, %d error(s).', $synced, $errors));

        return $errors > 0;
    }

    private function syncPendingOrders(SymfonyStyle $io): bool
    {
        $pendingOrders = $this->em->getRepository(BilletBuyer::class)->findBy([
            'status' => BilletBuyer::STATUS_PENDING,
        ]);

        if (0 === \count($pendingOrders)) {
            $io->info('No pending orders to reconcile.');

            return false;
        }

        $io->info(sprintf('Checking %d pending order(s) on Stripe...', \count($pendingOrders)));

        $processed = 0;
        $errors = 0;

        foreach ($pendingOrders as $order) {
            $paymentIntentId = $order->getStripeSessionId();

            $stripeAccountId = $order->getEvent()?->getAccount()?->getStripeAccountId();

            if (!$paymentIntentId || !$stripeAccountId) {
                $io->text(sprintf('  [<fg=yellow>SKIP</>] Order #%s — no payment intent ID or Stripe account', $order->getOrderNumber()));
                continue;
            }

            try {
                $paymentIntent = $this->stripeService->retrievePaymentIntent($paymentIntentId, $stripeAccountId);
                $stripeStatus = $paymentIntent->status;

                match ($stripeStatus) {
                    'succeeded' => $this->handleSucceeded($order, $paymentIntent, $io),
                    'canceled' => $this->handleCancelled($order, $io),
                    'requires_payment_method' => $this->handleFailed($order, $paymentIntent, $io),
                    default => $io->text(sprintf(
                        '  [<fg=blue>PENDING</>] Order #%s — Stripe status: %s',
                        $order->getOrderNumber(),
                        $stripeStatus,
                    )),
                };

                ++$processed;
            } catch (\Throwable $e) {
                $io->text(sprintf(
                    '  [<fg=red>ERROR</>] Order #%s — %s',
                    $order->getOrderNumber(),
                    $e->getMessage(),
                ));
                ++$errors;
            }
        }

        $this->em->flush();

        $io->success(sprintf('Orders: %d checked, %d error(s).', $processed, $errors));

        return $errors > 0;
    }

    private function handleSucceeded(BilletBuyer $order, \Stripe\PaymentIntent $paymentIntent, SymfonyStyle $io): void
    {
        $debtOrganizerId = $paymentIntent->metadata->debt_organizer_id ?? null;
        if ($debtOrganizerId) {
            $organizer = $this->em->getRepository(User::class)->find((int) $debtOrganizerId);
            if ($organizer) {
                $organizer->reduceDebt($paymentIntent->amount ?? 0);
            }
        }

        $this->billetOrderService->generateOrderTickets($order);
        $this->billetOrderService->generateAndSendTickets($order);
        $this->billetOrderService->notifyOrganizer($order);

        $io->text(sprintf('  [<fg=green>PAID</>] Order #%s — tickets generated and sent', $order->getOrderNumber()));
    }

    private function handleCancelled(BilletBuyer $order, SymfonyStyle $io): void
    {
        $order->setStatus(BilletBuyer::STATUS_CANCELLED);
        $this->em->flush();

        $this->audit->log('payment_cancelled_sync', 'BilletBuyer', $order->getId(), [
            'orderNumber' => $order->getOrderNumber(),
        ]);

        $io->text(sprintf('  [<fg=red>CANCELLED</>] Order #%s', $order->getOrderNumber()));
    }

    private function handleFailed(BilletBuyer $order, \Stripe\PaymentIntent $paymentIntent, SymfonyStyle $io): void
    {
        $errorMessage = $paymentIntent->last_payment_error->message ?? 'Paiement refuse';

        $order->setStatus(BilletBuyer::STATUS_CANCELLED);
        $this->em->flush();

        $this->audit->log('payment_failed_sync', 'BilletBuyer', $order->getId(), [
            'orderNumber' => $order->getOrderNumber(),
            'error' => $errorMessage,
        ]);

        if ($order->getEmail()) {
            $this->mailerService->sendEmail(
                $order->getEmail(),
                'Echec de paiement - '.$order->getEvent()->getTitle(),
                'Votre paiement pour la commande '.$order->getOrderNumber().' a echoue : '.$errorMessage,
            );
        }

        $io->text(sprintf('  [<fg=red>FAILED</>] Order #%s — %s', $order->getOrderNumber(), $errorMessage));
    }
}
