<?php

namespace App\Service;

use App\Entity\PriceAutomatic;
use App\Repository\PriceAutomaticRepository;
use Doctrine\ORM\EntityManagerInterface;
use Stripe\Price;
use Stripe\StripeClient;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * @codeCoverageIgnore
 */
class StripePriceService
{
    private StripeClient $stripe;

    public function __construct(
        private EntityManagerInterface $em,
        private PriceAutomaticRepository $repository,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSecret,
    ) {
        $this->stripe = new StripeClient($stripeSecret);
    }

    /**
     * Synchronise un tarif avec Stripe : cree ou met a jour le produit + prix.
     */
    public function syncPrice(PriceAutomatic $price): void
    {
        $productId = $this->ensureProduct($price);

        // Prix unique
        if (null === $price->getStripeId() || '' === $price->getStripeId()) {
            $stripePrice = $this->createStripePrice($productId, $price->getPriceHt(), false);
            $price->setStripeId($stripePrice->id);
        } else {
            $this->updateStripePriceIfNeeded($price->getStripeId(), $price->getPriceHt(), $productId);
        }

        // Prix abonnement (si monthPrice > 0)
        if ((float) $price->getMonthPrice() > 0) {
            if (null === $price->getStripeAbonnementId() || '' === $price->getStripeAbonnementId()) {
                $stripePrice = $this->createStripePrice($productId, $price->getMonthPrice(), true, $price->getPeriod());
                $price->setStripeAbonnementId($stripePrice->id);
            } else {
                $this->updateStripePriceIfNeeded($price->getStripeAbonnementId(), $price->getMonthPrice(), $productId);
            }
        }

        $this->em->flush();
    }

    /**
     * Synchronise tous les tarifs avec Stripe.
     *
     * @return array{synced: int, errors: list<string>}
     */
    public function syncAll(): array
    {
        $prices = $this->repository->findAll();
        $synced = 0;
        $errors = [];

        foreach ($prices as $price) {
            try {
                $this->syncPrice($price);
                ++$synced;
            } catch (\Throwable $e) {
                $errors[] = $price->getType().': '.$e->getMessage();
            }
        }

        return ['synced' => $synced, 'errors' => $errors];
    }

    /**
     * S'assure qu'un produit Stripe existe pour ce tarif.
     * Utilise le type comme metadata pour retrouver le produit.
     */
    private function ensureProduct(PriceAutomatic $price): string
    {
        // Chercher un produit existant par metadata
        $products = $this->stripe->products->search([
            'query' => "metadata['price_auto_type']:'{$price->getType()}'",
        ]);

        if (\count($products->data) > 0) {
            $product = $products->data[0];

            // Mettre a jour le nom/description si necessaire
            if ($product->name !== $price->getTitle() || ($product->description ?? '') !== ($price->getDescription() ?? '')) {
                $this->stripe->products->update($product->id, [
                    'name' => $price->getTitle(),
                    'description' => $price->getDescription() ?? '',
                ]);
            }

            return $product->id;
        }

        // Creer le produit
        $product = $this->stripe->products->create([
            'name' => $price->getTitle(),
            'description' => $price->getDescription() ?? '',
            'metadata' => [
                'price_auto_type' => $price->getType(),
                'price_auto_id' => (string) $price->getId(),
            ],
        ]);

        return $product->id;
    }

    private function createStripePrice(string $productId, string $amountHt, bool $recurring, int $periodMonths = 1): Price
    {
        $amountCents = (int) (int) round((float) $amountHt * 100);

        $params = [
            'product' => $productId,
            'unit_amount' => $amountCents,
            'currency' => 'eur',
            'tax_behavior' => 'exclusive',
        ];

        if ($recurring) {
            $interval = 'month';
            $intervalCount = $periodMonths;

            if ($periodMonths >= 12 && 0 === $periodMonths % 12) {
                $interval = 'year';
                $intervalCount = $periodMonths / 12;
            }

            $params['recurring'] = [
                'interval' => $interval,
                'interval_count' => $intervalCount,
            ];
        }

        return $this->stripe->prices->create($params);
    }

    /**
     * Stripe ne permet pas de modifier le montant d'un prix.
     * Si le montant a change, on archive l'ancien et on en cree un nouveau.
     */
    private function updateStripePriceIfNeeded(string $stripePriceId, string $amountHt, string $productId): void
    {
        try {
            $existingPrice = $this->stripe->prices->retrieve($stripePriceId);
            $expectedCents = (int) (int) round((float) $amountHt * 100);

            if ($existingPrice->unit_amount !== $expectedCents) {
                // Archiver l'ancien prix
                $this->stripe->prices->update($stripePriceId, ['active' => false]);

                // Creer un nouveau prix
                $isRecurring = null !== $existingPrice->recurring;
                $period = $isRecurring ? ($existingPrice->recurring->interval_count ?? 1) : 1;
                $newPrice = $this->createStripePrice($productId, $amountHt, $isRecurring, $period);

                // Le caller devra mettre a jour l'ID - on le fait via une exception controlée
                // En pratique, on met a jour directement dans syncPrice()
            }
        } catch (\Throwable) {
            // Prix introuvable ou erreur, on ignore
        }
    }
}
