<?php

namespace App\Controller\Admin;

use App\Entity\Advert;
use App\Entity\CustomerContact;
use App\Entity\Devis;
use App\Entity\Domain;
use App\Entity\Facture;
use App\Entity\StripeWebhookSecret;
use App\Entity\Website;
use App\Repository\CustomerRepository;
use App\Repository\PriceAutomaticRepository;
use App\Repository\RevendeurRepository;
use App\Repository\StripeWebhookSecretRepository;
use App\Service\MeilisearchService;
use App\Service\StripePriceService;
use App\Service\StripeWebhookService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/admin/sync', name: 'app_admin_sync_')]
#[IsGranted('ROLE_ROOT')]
class SyncController extends AbstractController
{
    #[Route('', name: 'index')]
    public function index(CustomerRepository $customerRepository, RevendeurRepository $revendeurRepository, PriceAutomaticRepository $priceRepository, StripeWebhookSecretRepository $secretRepository, EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        $prices = $priceRepository->findAll();
        $stripeSynced = 0;
        $stripeNotSynced = 0;

        foreach ($prices as $price) {
            if (null !== $price->getStripeId() && '' !== $price->getStripeId()) {
                ++$stripeSynced;
            } else {
                ++$stripeNotSynced;
            }
        }

        $webhookSecrets = $secretRepository->findAll();

        $customers = $customerRepository->findAll();
        $customersSynced = 0;
        $customersNotSynced = 0;
        foreach ($customers as $c) {
            if (null !== $c->getStripeCustomerId() && '' !== $c->getStripeCustomerId()) {
                ++$customersSynced;
            } else {
                ++$customersNotSynced;
            }
        }

        return $this->render('admin/sync/index.html.twig', [
            'totalCustomers' => \count($customers),
            'totalRevendeurs' => $revendeurRepository->count([]),
            'totalPrices' => \count($prices),
            'stripeSynced' => $stripeSynced,
            'stripeNotSynced' => $stripeNotSynced,
            'customersSynced' => $customersSynced,
            'customersNotSynced' => $customersNotSynced,
            'totalContacts' => $em->getRepository(CustomerContact::class)->count([]),
            'totalDomains' => $em->getRepository(Domain::class)->count([]),
            'totalWebsites' => $em->getRepository(Website::class)->count([]),
            'totalDevis' => $em->getRepository(Devis::class)->count([]),
            'totalAdverts' => $em->getRepository(Advert::class)->count([]),
            'totalFactures' => $em->getRepository(Facture::class)->count([]),
            'webhookSecrets' => $webhookSecrets,
            'msCustomers' => $meilisearch->getIndexCount('customer'),
            'msResellers' => $meilisearch->getIndexCount('reseller'),
            'msPrices' => $meilisearch->getIndexCount('price_auto'),
            'msContacts' => $meilisearch->getIndexCount('customer_contact'),
            'msDomains' => $meilisearch->getIndexCount('customer_ndd'),
            'msWebsites' => $meilisearch->getIndexCount('customer_website'),
            'msDevis' => $meilisearch->getIndexCount('customer_devis'),
            'msAdverts' => $meilisearch->getIndexCount('customer_advert'),
            'msFactures' => $meilisearch->getIndexCount('customer_facture'),
        ]);
    }

    #[Route('/purge-indexes', name: 'purge_indexes', methods: ['POST'])]
    public function purgeIndexes(MeilisearchService $meilisearch): Response
    {
        $meilisearch->purgeAllIndexes();
        $this->addFlash('success', 'Tous les index Meilisearch ont ete purges.');

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/customers', name: 'customers', methods: ['POST'])]
    public function syncCustomers(CustomerRepository $customerRepository, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $customers = $customerRepository->findAll();
            foreach ($customers as $customer) {
                $meilisearch->indexCustomer($customer);
            }
            $this->addFlash('success', \count($customers).' client(s) synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync clients : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/contacts', name: 'contacts', methods: ['POST'])]
    public function syncContacts(EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $contacts = $em->getRepository(CustomerContact::class)->findAll();
            foreach ($contacts as $contact) {
                $meilisearch->indexContact($contact);
            }
            $this->addFlash('success', \count($contacts).' contact(s) synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync contacts : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/domains', name: 'domains', methods: ['POST'])]
    public function syncDomains(EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $domains = $em->getRepository(Domain::class)->findAll();
            foreach ($domains as $domain) {
                $meilisearch->indexDomain($domain);
            }
            $this->addFlash('success', \count($domains).' domaine(s) synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync domaines : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/websites', name: 'websites', methods: ['POST'])]
    public function syncWebsites(EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $websites = $em->getRepository(Website::class)->findAll();
            foreach ($websites as $website) {
                $meilisearch->indexWebsite($website);
            }
            $this->addFlash('success', \count($websites).' site(s) synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync sites : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/devis', name: 'devis', methods: ['POST'])]
    public function syncDevis(EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $items = $em->getRepository(Devis::class)->findAll();
            foreach ($items as $item) {
                $meilisearch->indexDevis($item);
            }
            $this->addFlash('success', \count($items).' devis synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync devis : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/adverts', name: 'adverts', methods: ['POST'])]
    public function syncAdverts(EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $items = $em->getRepository(Advert::class)->findAll();
            foreach ($items as $item) {
                $meilisearch->indexAdvert($item);
            }
            $this->addFlash('success', \count($items).' avis de paiement synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync avis : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/factures', name: 'factures', methods: ['POST'])]
    public function syncFactures(EntityManagerInterface $em, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $items = $em->getRepository(Facture::class)->findAll();
            foreach ($items as $item) {
                $meilisearch->indexFacture($item);
            }
            $this->addFlash('success', \count($items).' facture(s) synchronisee(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync factures : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/revendeurs', name: 'revendeurs', methods: ['POST'])]
    public function syncRevendeurs(RevendeurRepository $revendeurRepository, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $revendeurs = $revendeurRepository->findAll();
            foreach ($revendeurs as $revendeur) {
                $meilisearch->indexRevendeur($revendeur);
            }
            $this->addFlash('success', \count($revendeurs).' revendeur(s) synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync revendeurs : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/prices', name: 'prices', methods: ['POST'])]
    public function syncPrices(PriceAutomaticRepository $priceRepository, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();
            $prices = $priceRepository->findAll();
            foreach ($prices as $price) {
                $meilisearch->indexPrice($price);
            }
            $this->addFlash('success', \count($prices).' tarif(s) synchronise(s) dans Meilisearch.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync tarifs : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/stripe/webhooks', name: 'stripe_webhooks', methods: ['POST'])]
    public function syncStripeWebhooks(
        StripeWebhookService $webhookService,
        StripeWebhookSecretRepository $secretRepository,
        EntityManagerInterface $em,
        #[Autowire(env: 'WEBHOOK_BASE_URL')] string $webhookBaseUrl,
    ): Response {
        if ('' === $webhookBaseUrl) {
            $this->addFlash('error', 'WEBHOOK_BASE_URL non configuree dans .env.local');

            return $this->redirectToRoute('app_admin_sync_index');
        }

        $result = $webhookService->createAllWebhooks(rtrim($webhookBaseUrl, '/'));

        $typeMap = [
            'Main Light' => StripeWebhookSecret::TYPE_MAIN_LIGHT,
            'Main Instant' => StripeWebhookSecret::TYPE_MAIN_INSTANT,
            'Connect Light' => StripeWebhookSecret::TYPE_CONNECT_LIGHT,
            'Connect Instant' => StripeWebhookSecret::TYPE_CONNECT_INSTANT,
        ];

        foreach ($result['created'] as $wh) {
            if ('exists' === $wh['status']) {
                $this->addFlash('success', $wh['type'].' : deja configure ('.$wh['id'].')');
            } else {
                $this->addFlash('success', $wh['type'].' : cree ('.$wh['id'].')');

                if (isset($wh['secret'], $typeMap[$wh['type']])) {
                    $dbType = $typeMap[$wh['type']];
                    $existing = $secretRepository->findByType($dbType);

                    if (null !== $existing) {
                        $existing->setSecret($wh['secret']);
                        $existing->setEndpointId($wh['id']);
                    } else {
                        $entity = new StripeWebhookSecret($dbType, $wh['secret'], $wh['id']);
                        $em->persist($entity);
                    }
                }
            }
        }

        $em->flush();

        foreach ($result['errors'] as $error) {
            $this->addFlash('error', 'Stripe Webhook : '.$error);
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/stripe/prices', name: 'stripe_prices', methods: ['POST'])]
    public function syncStripePrices(StripePriceService $stripePriceService): Response
    {
        $result = $stripePriceService->syncAll();

        if ([] === $result['errors']) {
            $this->addFlash('success', $result['synced'].' tarif(s) synchronise(s) avec Stripe.');
        } else {
            $this->addFlash('success', $result['synced'].' tarif(s) synchronise(s) avec Stripe.');
            foreach ($result['errors'] as $error) {
                $this->addFlash('error', 'Stripe : '.$error);
            }
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }

    /** @codeCoverageIgnore */
    #[Route('/stripe/customers', name: 'stripe_customers', methods: ['POST'])]
    public function syncStripeCustomers(
        CustomerRepository $customerRepository,
        EntityManagerInterface $em,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSecretKey,
    ): Response {
        if ('' === $stripeSecretKey || 'sk_test_***' === $stripeSecretKey) {
            $this->addFlash('error', 'STRIPE_SK non configuree.');

            return $this->redirectToRoute('app_admin_sync_index');
        }

        \Stripe\Stripe::setApiKey($stripeSecretKey);
        $customers = $customerRepository->findAll();
        $created = 0;
        $updated = 0;
        $errors = 0;

        foreach ($customers as $customer) {
            try {
                $params = [
                    'email' => $customer->getEmail(),
                    'name' => $customer->getRaisonSociale() ?? $customer->getFullName(),
                    'phone' => $customer->getPhone(),
                    'metadata' => [
                        'crm_user_id' => (string) $customer->getUser()->getId(),
                        'siret' => $customer->getSiret() ?? '',
                        'code_comptable' => $customer->getCodeComptable() ?? '',
                    ],
                ];

                if (null !== $customer->getStripeCustomerId() && '' !== $customer->getStripeCustomerId()) {
                    \Stripe\Customer::update($customer->getStripeCustomerId(), $params);
                    ++$updated;
                } else {
                    $stripeCustomer = \Stripe\Customer::create($params);
                    $customer->setStripeCustomerId($stripeCustomer->id);
                    ++$created;
                }
            } catch (\Throwable $e) {
                $this->addFlash('error', 'Stripe client '.$customer->getEmail().' : '.$e->getMessage());
                ++$errors;
            }
        }

        $em->flush();
        $this->addFlash('success', $created.' client(s) cree(s), '.$updated.' mis a jour dans Stripe'.(0 < $errors ? ', '.$errors.' erreur(s)' : '').'.');

        return $this->redirectToRoute('app_admin_sync_index');
    }

    #[Route('/all', name: 'all', methods: ['POST'])]
    public function syncAll(CustomerRepository $customerRepository, RevendeurRepository $revendeurRepository, PriceAutomaticRepository $priceRepository, MeilisearchService $meilisearch): Response
    {
        try {
            $meilisearch->setupIndexes();

            $customers = $customerRepository->findAll();
            foreach ($customers as $customer) {
                $meilisearch->indexCustomer($customer);
            }

            $revendeurs = $revendeurRepository->findAll();
            foreach ($revendeurs as $revendeur) {
                $meilisearch->indexRevendeur($revendeur);
            }

            $prices = $priceRepository->findAll();
            foreach ($prices as $price) {
                $meilisearch->indexPrice($price);
            }

            $this->addFlash('success', \count($customers).' client(s), '.\count($revendeurs).' revendeur(s) et '.\count($prices).' tarif(s) synchronise(s).');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur sync : '.$e->getMessage());
        }

        return $this->redirectToRoute('app_admin_sync_index');
    }
}
