<?php

namespace App\Controller\Admin;

use App\Entity\Advert;
use App\Entity\Customer;
use App\Entity\Domain;
use App\Entity\Echeancier;
use App\Entity\User;
use App\Repository\CustomerRepository;
use App\Repository\RevendeurRepository;
use App\Service\CloudflareService;
use App\Service\DnsCheckService;
use App\Service\EsyMailDnsService;
use App\Service\MailerService;
use App\Service\MeilisearchService;
use App\Service\OvhService;
use App\Service\UserManagementService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Twig\Environment;

#[Route('/admin/clients', name: 'app_admin_clients_')]
#[IsGranted('ROLE_EMPLOYE')]
class ClientsController extends AbstractController
{
    private const WELCOME_SUBJECT = 'CRM E-Cosplay - Bienvenue dans votre espace client';
    private const WELCOME_TEMPLATE = 'emails/client_created.html.twig';

    #[Route('', name: 'index')]
    public function index(CustomerRepository $customerRepository, EntityManagerInterface $em): Response
    {
        $customers = $customerRepository->findBy([], ['createdAt' => 'DESC']);
        $customersInfo = $this->buildCustomersInfo($customers, $em);

        return $this->render('admin/clients/index.html.twig', [
            'customers' => $customers,
            'customersInfo' => $customersInfo,
        ]);
    }

    #[Route('/create', name: 'create')]
    public function create(
        Request $request,
        CustomerRepository $customerRepository,
        RevendeurRepository $revendeurRepository,
        EntityManagerInterface $em,
        MeilisearchService $meilisearch,
        UserManagementService $userService,
        LoggerInterface $logger,
        HttpClientInterface $httpClient,
        MailerService $mailer,
        Environment $twig,
        #[Autowire(env: 'STRIPE_SK')] string $stripeSecretKey,
    ): Response {
        if ('POST' !== $request->getMethod()) {
            return $this->render('admin/clients/create.html.twig', [
                'revendeurs' => $revendeurRepository->findBy(['isActive' => true], ['codeRevendeur' => 'ASC']),
            ]);
        }

        try {
            $firstName = trim($request->request->getString('firstName'));
            $lastName = trim($request->request->getString('lastName'));
            $email = trim($request->request->getString('email'));

            $user = $userService->createBaseUser($email, $firstName, $lastName, ['ROLE_CUSTOMER']);

            $customer = new Customer($user);
            $this->populateCustomerData($request, $customer);
            $this->geocodeIfNeeded($customer, $httpClient);

            $this->setupStripeCustomer($customer, $stripeSecretKey);

            $em->persist($customer);
            $em->flush();

            $this->finalizeStripeMetadata($customer, $user, $stripeSecretKey);

            $codeComptable = trim($request->request->getString('codeComptable'));
            if ('' !== $codeComptable) {
                $customer->setCodeComptable(str_starts_with($codeComptable, 'EC-') ? $codeComptable : 'EC-'.$codeComptable);
            } else {
                $customer->setCodeComptable($customerRepository->generateUniqueCodeComptable($customer));
            }
            $em->flush();

            try {
                $meilisearch->indexCustomer($customer);
            } catch (\Throwable $e) {
                $logger->error('Meilisearch: impossible d\'indexer le client '.$customer->getId().': '.$e->getMessage());
            }
            $this->ensureDefaultContact($customer, $em);

            $setPasswordUrl = $this->generateUrl('app_set_password', ['token' => $user->getTempPassword()], UrlGeneratorInterface::ABSOLUTE_URL);
            $mailer->sendEmail(
                $user->getEmail(),
                self::WELCOME_SUBJECT,
                $twig->render(self::WELCOME_TEMPLATE, [
                    'firstName' => $user->getFirstName(),
                    'email' => $user->getEmail(),
                    'setPasswordUrl' => $setPasswordUrl,
                ]),
            );

            $this->addFlash('success', 'Client '.$customer->getFullName().' cree. Email de bienvenue envoye.');

            return $this->redirectToRoute('app_admin_clients_index');
        } catch (\InvalidArgumentException $e) {
            $this->addFlash('error', $e->getMessage());
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur : '.$e->getMessage());
        }

        return $this->render('admin/clients/create.html.twig');
    }

    /**
     * @param list<Customer> $customers
     *
     * @return array<int, array{sites: int, domains: int, emails: int, esySign: bool, esyNewsletter: bool, esyMail: bool, unpaid: int}>
     */
    private function buildCustomersInfo(array $customers, EntityManagerInterface $em): array
    {
        $domainRepo = $em->getRepository(Domain::class);
        $emailRepo = $em->getRepository(\App\Entity\DomainEmail::class);
        $websiteRepo = $em->getRepository(\App\Entity\Website::class);
        $info = [];

        foreach ($customers as $customer) {
            $domains = $domainRepo->findBy(['customer' => $customer]);
            $emailCount = 0;
            foreach ($domains as $domain) {
                $emailCount += $emailRepo->count(['domain' => $domain]);
            }

            $info[$customer->getId()] = [
                'sites' => $websiteRepo->count(['customer' => $customer]),
                'domains' => \count($domains),
                'emails' => $emailCount,
                'esySign' => false,
                'esyNewsletter' => false,
                'esyMail' => $emailCount > 0,
                'unpaid' => 0,
            ];
        }

        return $info;
    }

    /**
     * @param list<Domain> $domains
     *
     * @return array<int, array{esyMail: bool, emailCount: int, esyMailer: bool, configDnsEsyMail: bool, configDnsEsyMailer: bool}>
     */
    private function buildDomainsInfo(array $domains, EntityManagerInterface $em, EsyMailDnsService $dnsService, bool $checkDns = false): array
    {
        $emailRepo = $em->getRepository(\App\Entity\DomainEmail::class);
        $info = [];

        foreach ($domains as $domain) {
            $emailCount = $emailRepo->count(['domain' => $domain]);
            $configMail = false;
            $configMailer = false;

            if ($checkDns) {
                $configMail = $dnsService->checkDnsEsyMail($domain->getFqdn())['ok'];
                $configMailer = $dnsService->checkDnsEsyMailer($domain->getFqdn())['ok'];
            }

            $info[$domain->getId()] = [
                'esyMail' => $emailCount > 0,
                'emailCount' => $emailCount,
                'esyMailer' => false,
                'configDnsEsyMail' => $configMail,
                'configDnsEsyMailer' => $configMailer,
            ];
        }

        return $info;
    }

    private function ensureDefaultContact(Customer $customer, EntityManagerInterface $em): void
    {
        $contactRepo = $em->getRepository(\App\Entity\CustomerContact::class);
        $existing = $contactRepo->findBy(['customer' => $customer]);

        if ([] !== $existing) {
            return;
        }

        $firstName = $customer->getFirstName();
        $lastName = $customer->getLastName();

        if (null === $firstName || null === $lastName || '' === $firstName || '' === $lastName) {
            return;
        }

        $contact = new \App\Entity\CustomerContact($customer, $firstName, $lastName);
        $contact->setEmail($customer->getEmail());
        $contact->setPhone($customer->getPhone());
        $contact->setRole('Directeur');
        $contact->setIsBillingEmail(true);

        $em->persist($contact);
        $em->flush();
    }

    private function populateCustomerData(Request $request, Customer $customer): void
    {
        $customer->setFirstName(trim($request->request->getString('firstName')));
        $customer->setLastName(trim($request->request->getString('lastName')));
        $customer->setEmail(trim($request->request->getString('email')));
        $customer->setPhone(trim($request->request->getString('phone')) ?: null);
        $customer->setRaisonSociale(trim($request->request->getString('raisonSociale')) ?: null);
        $customer->setSiret(trim($request->request->getString('siret')) ?: null);
        $customer->setRcs(trim($request->request->getString('rcs')) ?: null);
        $customer->setNumTva(trim($request->request->getString('numTva')) ?: null);
        $customer->setApe(trim($request->request->getString('ape')) ?: null);
        $customer->setRna(trim($request->request->getString('rna')) ?: null);
        $customer->setAddress(trim($request->request->getString('address')) ?: null);
        $customer->setAddress2(trim($request->request->getString('address2')) ?: null);
        $customer->setZipCode(trim($request->request->getString('zipCode')) ?: null);
        $customer->setCity(trim($request->request->getString('city')) ?: null);
        $customer->setTypeCompany(trim($request->request->getString('typeCompany')) ?: null);
        $customer->setRevendeurCode(trim($request->request->getString('revendeurCode')) ?: null);
        $customer->setGeoLat(trim($request->request->getString('geoLat')) ?: null);
        $customer->setGeoLong(trim($request->request->getString('geoLong')) ?: null);
    }

    /** @codeCoverageIgnore */
    private function geocodeIfNeeded(Customer $customer, HttpClientInterface $httpClient): void
    {
        if (null !== $customer->getGeoLat() || null === $customer->getAddress()) {
            return;
        }

        $q = implode(' ', array_filter([$customer->getAddress(), $customer->getZipCode(), $customer->getCity()]));

        try {
            $response = $httpClient->request('GET', 'https://data.geopf.fr/geocodage/search', [
                'query' => ['q' => $q, 'limit' => 1],
            ]);
            $data = $response->toArray();
            $coords = $data['features'][0]['geometry']['coordinates'] ?? null;

            if (null !== $coords) {
                $customer->setGeoLong((string) $coords[0]);
                $customer->setGeoLat((string) $coords[1]);
            }
        } catch (\Throwable) {
            // Geocoding is best-effort, ignore failures silently
        }
    }

    /** @codeCoverageIgnore */
    private function setupStripeCustomer(Customer $customer, string $stripeSecretKey): void
    {
        if ('' === $stripeSecretKey || 'sk_test_***' === $stripeSecretKey) {
            return;
        }

        \Stripe\Stripe::setApiKey($stripeSecretKey);
        $params = [
            'email' => $customer->getEmail(),
            'name' => $customer->getRaisonSociale() ?? $customer->getFullName(),
            'phone' => $customer->getPhone(),
            'metadata' => [
                'crm_user_id' => 'pending',
                'siret' => $customer->getSiret() ?? '',
                'code_comptable' => $customer->getCodeComptable() ?? '',
            ],
        ];

        $address = array_filter([
            'line1' => $customer->getAddress(),
            'line2' => $customer->getAddress2(),
            'postal_code' => $customer->getZipCode(),
            'city' => $customer->getCity(),
            'country' => 'FR',
        ]);
        if (isset($address['line1'])) {
            $params['address'] = $address;
        }

        if ($customer->getNumTva()) {
            $params['tax_id_data'] = [['type' => 'eu_vat', 'value' => $customer->getNumTva()]];
        }

        $stripeCustomer = \Stripe\Customer::create($params);
        $customer->setStripeCustomerId($stripeCustomer->id);
    }

    /** @codeCoverageIgnore */
    private function finalizeStripeMetadata(Customer $customer, User $user, string $stripeSecretKey): void
    {
        if (null === $customer->getStripeCustomerId() || '' === $stripeSecretKey || 'sk_test_***' === $stripeSecretKey) {
            return;
        }

        \Stripe\Stripe::setApiKey($stripeSecretKey);
        \Stripe\Customer::update($customer->getStripeCustomerId(), [
            'metadata' => [
                'crm_user_id' => (string) $user->getId(),
                'code_comptable' => $customer->getCodeComptable() ?? '',
            ],
        ]);
    }

    #[Route('/search', name: 'search', methods: ['GET'])]
    public function search(Request $request, MeilisearchService $meilisearch): JsonResponse
    {
        $query = trim($request->query->getString('q'));

        if ('' === $query) {
            return new JsonResponse([]);
        }

        return new JsonResponse($meilisearch->searchCustomers($query));
    }

    #[Route('/entreprise-search', name: 'entreprise_search', methods: ['GET'])]
    public function entrepriseSearch(Request $request, \App\Service\EntrepriseSearchService $searchService): JsonResponse
    {
        return $searchService->search(trim($request->query->getString('q')));
    }

    #[Route('/{id}', name: 'show')]
    public function show(Customer $customer, Request $request, EntityManagerInterface $em, OvhService $ovhService, CloudflareService $cloudflareService, DnsCheckService $dnsCheckService, EsyMailDnsService $esyMailDnsService, UserPasswordHasherInterface $passwordHasher, MailerService $mailer, Environment $twig): Response
    {
        $tab = $request->query->getString('tab', 'info');

        if ('POST' === $request->getMethod()) {
            if ('info' === $tab) {
                $this->populateCustomerData($request, $customer);
                $customer->setUpdatedAt(new \DateTimeImmutable());
                $em->flush();
                $this->addFlash('success', 'Informations mises a jour.');

                return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId()]);
            }

            return match ($tab) {
                'contacts' => $this->handleContactForm($request, $customer, $em),
                'ndd' => $this->handleDomainForm($request, $customer, $em, $ovhService, $cloudflareService, $dnsCheckService),
                'securite' => $this->handleSecurityForm($request, $customer, $em, $passwordHasher, $mailer, $twig),
                default => $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId(), 'tab' => $tab]),
            };
        }

        $this->ensureDefaultContact($customer, $em);
        $contacts = $em->getRepository(\App\Entity\CustomerContact::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $domains = $em->getRepository(Domain::class)->findBy(['customer' => $customer]);
        $domainsInfo = $this->buildDomainsInfo($domains, $em, $esyMailDnsService, 'ndd' === $tab);
        $websites = $em->getRepository(\App\Entity\Website::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $devisList = $em->getRepository(\App\Entity\Devis::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $advertsList = $em->getRepository(\App\Entity\Advert::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $facturesList = $em->getRepository(\App\Entity\Facture::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $echeancierList = $em->getRepository(\App\Entity\Echeancier::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $eflexList = $em->getRepository(\App\Entity\EFlex::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);
        $contratsList = $em->getRepository(\App\Entity\Contrat::class)->findBy(['customer' => $customer], ['createdAt' => 'DESC']);

        $trustStatus = $this->computeTrustStatus($advertsList, $echeancierList, $customer);

        return $this->render('admin/clients/show.html.twig', [
            'customer' => $customer,
            'contacts' => $contacts,
            'domains' => $domains,
            'domainsInfo' => $domainsInfo,
            'websites' => $websites,
            'devisList' => $devisList,
            'advertsList' => $advertsList,
            'facturesList' => $facturesList,
            'echeancierList' => $echeancierList,
            'eflexList' => $eflexList,
            'contratsList' => $contratsList,
            'tab' => $tab,
            'trustStatus' => $trustStatus,
        ]);
    }

    /**
     * Calcule le statut de confiance du client.
     *
     * Confiant : 0 impaye
     * Attention : 1 impaye (avis ou echeance)
     * Danger : echeancier annule avec rejets, ou 3+ avis impayes, ou 2+ impayes
     *
     * @param list<Advert>     $adverts
     * @param list<Echeancier> $echeanciers
     *
     * @return array{status: string, label: string, color: string, reason: string}
     */
    private function computeTrustStatus(array $adverts, array $echeanciers, Customer $customer): array
    {
        // Avertissements : 2nd = Attention, last = Danger
        $warningLevel = $customer->getWarningLevel();
        if ('last' === $warningLevel) {
            return ['status' => 'danger', 'label' => 'Danger', 'color' => 'red', 'reason' => 'Dernier avertissement envoye'];
        }
        if ('2nd' === $warningLevel) {
            return ['status' => 'attention', 'label' => 'Attention', 'color' => 'yellow', 'reason' => '2eme avertissement envoye'];
        }

        // Compter les avis impayes (envoyes mais pas acceptes/refuses/annules)
        $nbUnpaidAdverts = 0;
        foreach ($adverts as $advert) {
            if (\in_array($advert->getState(), [Advert::STATE_CREATED, Advert::STATE_SEND], true)) {
                ++$nbUnpaidAdverts;
            }
        }

        // Verifier les echeanciers annules avec rejets
        $hasCancelledWithRejects = false;
        $nbUnpaidEcheances = 0;
        foreach ($echeanciers as $echeancier) {
            if (Echeancier::STATE_CANCELLED === $echeancier->getState() && $echeancier->getNbFailed() > 0) {
                $hasCancelledWithRejects = true;
            }
            if (\in_array($echeancier->getState(), [Echeancier::STATE_ACTIVE, Echeancier::STATE_PENDING_SETUP, Echeancier::STATE_SIGNED], true)) {
                foreach ($echeancier->getLines() as $line) {
                    if ('ok' !== $line->getState()) {
                        ++$nbUnpaidEcheances;
                    }
                }
            }
        }

        $totalUnpaid = $nbUnpaidAdverts + ($nbUnpaidEcheances > 0 ? 1 : 0);

        // Danger
        if ($hasCancelledWithRejects) {
            return ['status' => 'danger', 'label' => 'Danger', 'color' => 'red', 'reason' => 'Echeancier annule suite a des rejets de prelevement'];
        }
        if ($nbUnpaidAdverts >= 3) {
            return ['status' => 'danger', 'label' => 'Danger', 'color' => 'red', 'reason' => $nbUnpaidAdverts.' avis de paiement impayes'];
        }
        if ($totalUnpaid >= 2) {
            return ['status' => 'danger', 'label' => 'Danger', 'color' => 'red', 'reason' => $totalUnpaid.' impayes (avis + echeanciers)'];
        }

        // Attention (1er avertissement ou 1 impaye)
        if ('1st' === $warningLevel) {
            return ['status' => 'attention', 'label' => 'Attention', 'color' => 'yellow', 'reason' => '1er avertissement envoye'];
        }
        if ($totalUnpaid >= 1) {
            $reason = $nbUnpaidAdverts > 0 ? $nbUnpaidAdverts.' avis impaye(s)' : 'Echeancier en cours';

            return ['status' => 'attention', 'label' => 'Attention', 'color' => 'yellow', 'reason' => $reason];
        }

        return ['status' => 'confiant', 'label' => 'Confiant', 'color' => 'green', 'reason' => 'Aucun impaye'];
    }

    private function handleContactForm(Request $request, Customer $customer, EntityManagerInterface $em): Response
    {
        $action = $request->request->getString('contact_action');

        if ('create' === $action) {
            $this->persistNewContact($request, $customer, $em);
        } elseif ('delete' === $action) {
            $contactId = $request->request->getInt('contact_id');
            $contact = $em->getRepository(\App\Entity\CustomerContact::class)->find($contactId);
            if (null !== $contact && $contact->getCustomer() === $customer) {
                $em->remove($contact);
                $em->flush();
                $this->addFlash('success', 'Contact supprime.');
            }
        }

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId(), 'tab' => 'contacts']);
    }

    private function persistNewContact(Request $request, Customer $customer, EntityManagerInterface $em): void
    {
        $firstName = trim($request->request->getString('contact_firstName'));
        $lastName = trim($request->request->getString('contact_lastName'));

        if ('' === $firstName || '' === $lastName) {
            return;
        }

        $contact = new \App\Entity\CustomerContact($customer, $firstName, $lastName);
        $contact->setEmail(trim($request->request->getString('contact_email')) ?: null);
        $contact->setPhone(trim($request->request->getString('contact_phone')) ?: null);
        $contact->setRole(trim($request->request->getString('contact_role')) ?: null);
        $contact->setIsBillingEmail($request->request->getBoolean('contact_isBilling'));
        $em->persist($contact);
        $em->flush();
        $this->addFlash('success', 'Contact '.$firstName.' '.$lastName.' ajoute.');
    }

    private function handleDomainForm(Request $request, Customer $customer, EntityManagerInterface $em, OvhService $ovhService, CloudflareService $cloudflareService, DnsCheckService $dnsCheckService): Response
    {
        $action = $request->request->getString('domain_action');

        if ('create' === $action) {
            $fqdn = strtolower(trim($request->request->getString('domain_fqdn')));
            $registrar = $request->request->getString('domain_registrar') ?: null;

            if ('' === $fqdn) {
                $this->addFlash('error', 'Le nom de domaine est requis.');

                return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId(), 'tab' => 'ndd']);
            }

            $existing = $em->getRepository(Domain::class)->findOneBy(['fqdn' => $fqdn]);
            if (null !== $existing) {
                $this->addFlash('error', 'Le domaine '.$fqdn.' existe deja.');

                return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId(), 'tab' => 'ndd']);
            }

            $domain = new Domain($customer, $fqdn);
            $domain->setRegistrar($registrar);

            $this->autoDetectDomain($domain, $ovhService, $cloudflareService, $dnsCheckService);

            $em->persist($domain);
            $em->flush();

            $this->addFlash('success', 'Domaine '.$fqdn.' ajoute.');
        }

        if ('delete' === $action) {
            $domainId = $request->request->getInt('domain_id');
            $domain = $em->getRepository(Domain::class)->find($domainId);
            if (null !== $domain && $domain->getCustomer() === $customer) {
                $em->remove($domain);
                $em->flush();
                $this->addFlash('success', 'Domaine supprime.');
            }
        }

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId(), 'tab' => 'ndd']);
    }

    private function autoDetectDomain(Domain $domain, OvhService $ovhService, CloudflareService $cloudflareService, DnsCheckService $dnsCheckService): void
    {
        $fqdn = $domain->getFqdn();

        // Check OVH
        if ($ovhService->isDomainManaged($fqdn)) {
            $domain->setRegistrar('OVH');
            $domain->setIsGestion(true);
            $domain->setIsBilling(true);

            $serviceInfo = $ovhService->getDomainServiceInfo($fqdn);
            if (null !== $serviceInfo) {
                $domain->setExpiredAt(new \DateTimeImmutable($serviceInfo['expiration']));
                $domain->setUpdatedAt(new \DateTimeImmutable());
            }

            $zoneInfo = $ovhService->getZoneInfo($fqdn);
            if (null !== $zoneInfo) {
                $domain->setZoneCloudflare(null);
                $domain->setZoneIdCloudflare(null);
            }
        }

        // Check Cloudflare
        if ($cloudflareService->isAvailable()) {
            $zoneId = $cloudflareService->getZoneId($fqdn);
            if (null !== $zoneId) {
                $domain->setZoneCloudflare('active');
                $domain->setZoneIdCloudflare($zoneId);

                if (null === $domain->getRegistrar()) {
                    $domain->setRegistrar('Cloudflare');
                    $domain->setIsGestion(true);
                    $domain->setIsBilling(true);
                }
            }
        }

        // Fallback RDAP pour l'expiration si pas encore trouvée
        if (null === $domain->getExpiredAt()) {
            $expiration = $dnsCheckService->getExpirationDate($fqdn);
            if (null !== $expiration) {
                $domain->setExpiredAt($expiration);
            }
        }
    }

    private function handleSecurityForm(Request $request, Customer $customer, EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasher, MailerService $mailer, Environment $twig): Response
    {
        $action = $request->request->getString('security_action');
        $user = $customer->getUser();

        if ('send_reset_password' === $action) {
            $tempPassword = bin2hex(random_bytes(8));
            $user->setPassword($passwordHasher->hashPassword($user, $tempPassword));
            $user->setTempPassword($tempPassword);
            $em->flush();

            $setPasswordUrl = $this->generateUrl('app_set_password', ['token' => $user->getTempPassword()], UrlGeneratorInterface::ABSOLUTE_URL);
            $mailer->sendEmail(
                $user->getEmail(),
                self::WELCOME_SUBJECT,
                $twig->render(self::WELCOME_TEMPLATE, [
                    'firstName' => $user->getFirstName(),
                    'email' => $user->getEmail(),
                    'setPasswordUrl' => $setPasswordUrl,
                ]),
            );
            $this->addFlash('success', 'Un email de reinitialisation a ete envoye a '.$user->getEmail().'.');
        }

        if ('disable_2fa' === $action) {
            $user->setIsEmailAuthEnabled(false);
            $user->setIsGoogleAuthEnabled(false);
            $user->setGoogleAuthenticatorSecret(null);
            $user->setBackupCodes([]);
            $em->flush();
            $this->addFlash('success', 'Authentification a deux facteurs desactivee.');
        }

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId(), 'tab' => 'securite']);
    }

    #[Route('/{id}/resend-welcome', name: 'resend_welcome', methods: ['POST'])]
    public function resendWelcome(Customer $customer, MailerService $mailer, Environment $twig): Response
    {
        $user = $customer->getUser();

        if (!$user->hasTempPassword()) {
            $this->addFlash('error', 'Le mot de passe temporaire n\'est plus disponible. Le client a deja active son compte.');

            return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId()]);
        }

        $setPasswordUrl = $this->generateUrl('app_set_password', ['token' => $user->getTempPassword()], UrlGeneratorInterface::ABSOLUTE_URL);
        $mailer->sendEmail(
            $user->getEmail(),
            self::WELCOME_SUBJECT,
            $twig->render(self::WELCOME_TEMPLATE, [
                'firstName' => $user->getFirstName(),
                'email' => $user->getEmail(),
                'setPasswordUrl' => $setPasswordUrl,
            ]),
        );
        $this->addFlash('success', 'Email de bienvenue renvoye a '.$user->getEmail().'.');

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $customer->getId()]);
    }

    #[Route('/{id}/toggle', name: 'toggle', methods: ['POST'])]
    public function toggle(Customer $customer, EntityManagerInterface $em, MeilisearchService $meilisearch, LoggerInterface $logger): Response
    {
        $newState = $customer->isActive() ? Customer::STATE_SUSPENDED : Customer::STATE_ACTIVE;
        $customer->setState($newState);
        $em->flush();

        try {
            $meilisearch->indexCustomer($customer);
        } catch (\Throwable $e) {
            $logger->error('Meilisearch: impossible d\'indexer le client '.$customer->getId().': '.$e->getMessage());
        }

        $this->addFlash('success', 'Client '.($customer->isActive() ? 'active' : 'suspendu').'.');

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

    #[Route('/{id}/delete', name: 'delete', methods: ['POST'])]
    public function delete(Customer $customer, EntityManagerInterface $em): Response
    {
        if ($customer->isPendingDelete()) {
            $this->addFlash('error', 'Ce client est deja en attente de suppression.');

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

        $customer->setState(Customer::STATE_PENDING_DELETE);
        $em->flush();

        $this->addFlash('success', 'Client "'.$customer->getFullName().'" marque pour suppression. Il sera supprime automatiquement cette nuit.');

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

    /**
     * Envoie un avertissement au client (1st, 2nd, last).
     */
    #[Route('/{id}/send-warning/{level}', name: 'send_warning', requirements: ['id' => '\d+', 'level' => '1st|2nd|last'], methods: ['POST'])]
    #[IsGranted('ROLE_ROOT')]
    public function sendWarning(
        int $id,
        string $level,
        Request $request,
        EntityManagerInterface $em,
        \App\Service\DocuSealService $docuSeal,
        \Symfony\Component\HttpKernel\KernelInterface $kernel,
    ): Response {
        $customer = $em->getRepository(Customer::class)->find($id);
        if (null === $customer) {
            throw $this->createNotFoundException('Client introuvable');
        }

        if (null === $customer->getEmail()) {
            $this->addFlash('error', 'Email client introuvable.');

            return $this->redirectToRoute('app_admin_clients_show', ['id' => $id, 'tab' => 'controle']);
        }

        $reasons = $request->request->all('reasons');

        // Generer le PDF
        $pdf = new \App\Service\Pdf\ClientWarningPdf($kernel, $customer, $level, $reasons);
        $pdf->generate();

        $tmpPath = tempnam(sys_get_temp_dir(), 'warning_').'.pdf';
        $pdf->Output('F', $tmpPath);

        $warningLabels = [
            '1st' => '1er avertissement',
            '2nd' => '2eme avertissement',
            'last' => 'Dernier avertissement avant suspension',
        ];

        // Envoyer a DocuSeal pour auto-signature
        // Le webhook (doc_type=client_warning) enverra le mail avec le PDF signe
        // @codeCoverageIgnoreStart
        try {
            $pdfBase64 = base64_encode(file_get_contents($tmpPath));

            $docuSeal->getApi()->createSubmissionFromPdf([
                'name' => 'Avertissement '.$warningLabels[$level].' - '.$customer->getFullName(),
                'send_email' => false,
                'flatten' => true,
                'documents' => [[
                    'name' => 'avertissement-'.$level.'-'.$customer->getId().'.pdf',
                    'file' => 'data:application/pdf;base64,'.$pdfBase64,
                ]],
                'submitters' => [[
                    'email' => 'client@e-cosplay.fr',
                    'name' => 'Association E-Cosplay',
                    'role' => 'First Party',
                    'completed' => true,
                    'send_email' => false,
                    'values' => ['Sign' => $docuSeal->getLogoBase64()],
                    'metadata' => [
                        'doc_type' => 'client_warning',
                        'customer_id' => $customer->getId(),
                        'level' => $level,
                        'reasons' => implode(',', $reasons),
                    ],
                ]],
            ]);

            $this->addFlash('success', $warningLabels[$level].' envoye pour signature. Le client recevra le PDF signe automatiquement.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur DocuSeal : '.$e->getMessage());
        }
        // @codeCoverageIgnoreEnd

        @unlink($tmpPath);

        $customer->setWarningLevel($level);
        $customer->setWarningAt(new \DateTimeImmutable());
        $em->flush();

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $id, 'tab' => 'controle']);
    }

    /**
     * Reinitialise les avertissements du client.
     */
    /**
     * Envoie la notification de cloture (PDF signe via DocuSeal) - n'effectue PAS la suppression.
     */
    #[Route('/{id}/close-account', name: 'close_account', requirements: ['id' => '\d+'], methods: ['POST'])]
    #[IsGranted('ROLE_ROOT')]
    public function closeAccount(
        int $id,
        EntityManagerInterface $em,
        \App\Service\DocuSealService $docuSeal,
        \Symfony\Component\HttpKernel\KernelInterface $kernel,
    ): Response {
        $customer = $em->getRepository(Customer::class)->find($id);
        if (null === $customer) {
            throw $this->createNotFoundException('Client introuvable');
        }

        if (null === $customer->getEmail()) {
            $this->addFlash('error', 'Email client introuvable.');

            return $this->redirectToRoute('app_admin_clients_show', ['id' => $id, 'tab' => 'controle']);
        }

        // Generer le PDF de cloture
        $pdf = new \App\Service\Pdf\ClientClosurePdf($kernel, $customer);
        $pdf->generate();

        $tmpPath = tempnam(sys_get_temp_dir(), 'closure_').'.pdf';
        $pdf->Output('F', $tmpPath);

        // Envoyer a DocuSeal pour auto-signature
        // Le webhook (doc_type=client_closure) enverra le mail avec le PDF signe
        // @codeCoverageIgnoreStart
        try {
            $pdfBase64 = base64_encode(file_get_contents($tmpPath));

            $docuSeal->getApi()->createSubmissionFromPdf([
                'name' => 'Cloture compte - '.$customer->getFullName(),
                'send_email' => false,
                'flatten' => true,
                'documents' => [[
                    'name' => 'cloture-'.$customer->getId().'.pdf',
                    'file' => 'data:application/pdf;base64,'.$pdfBase64,
                ]],
                'submitters' => [[
                    'email' => 'client@e-cosplay.fr',
                    'name' => 'Association E-Cosplay',
                    'role' => 'First Party',
                    'completed' => true,
                    'send_email' => false,
                    'values' => ['Sign' => $docuSeal->getLogoBase64()],
                    'metadata' => [
                        'doc_type' => 'client_closure',
                        'customer_id' => $customer->getId(),
                    ],
                ]],
            ]);

            $this->addFlash('success', 'Notification de cloture envoyee pour signature. Le client recevra le PDF signe.');
        } catch (\Throwable $e) {
            $this->addFlash('error', 'Erreur DocuSeal : '.$e->getMessage());
        }
        // @codeCoverageIgnoreEnd

        @unlink($tmpPath);

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $id, 'tab' => 'controle']);
    }

    /**
     * Effectue la suspension reelle du compte (state = suspended).
     */
    #[Route('/{id}/suspend-account', name: 'suspend_account', requirements: ['id' => '\d+'], methods: ['POST'])]
    #[IsGranted('ROLE_ROOT')]
    public function suspendAccount(int $id, EntityManagerInterface $em): Response
    {
        $customer = $em->getRepository(Customer::class)->find($id);
        if (null === $customer) {
            throw $this->createNotFoundException('Client introuvable');
        }

        $customer->setState('suspended');
        $customer->setUpdatedAt(new \DateTimeImmutable());
        $em->flush();

        $this->addFlash('success', 'Compte de '.$customer->getFullName().' suspendu.');

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $id, 'tab' => 'controle']);
    }

    #[Route('/{id}/reset-warning', name: 'reset_warning', requirements: ['id' => '\d+'], methods: ['POST'])]
    #[IsGranted('ROLE_ROOT')]
    public function resetWarning(
        int $id,
        EntityManagerInterface $em,
        \App\Service\DocuSealService $docuSeal,
        \Symfony\Component\HttpKernel\KernelInterface $kernel,
    ): Response {
        $customer = $em->getRepository(Customer::class)->find($id);
        if (null === $customer) {
            throw $this->createNotFoundException('Client introuvable');
        }

        $customer->setWarningLevel(null);
        $customer->setWarningAt(null);
        $em->flush();

        // Generer le PDF de levee d'avertissement et envoyer a DocuSeal
        // Le webhook (doc_type=client_warning_reset) enverra le mail avec le PDF signe
        // @codeCoverageIgnoreStart
        if (null !== $customer->getEmail()) {
            try {
                $pdf = new \App\Service\Pdf\ClientWarningResetPdf($kernel, $customer);
                $pdf->generate();

                $tmpPath = tempnam(sys_get_temp_dir(), 'reset_').'.pdf';
                $pdf->Output('F', $tmpPath);

                $pdfBase64 = base64_encode(file_get_contents($tmpPath));

                $docuSeal->getApi()->createSubmissionFromPdf([
                    'name' => 'Levee avertissement - '.$customer->getFullName(),
                    'send_email' => false,
                    'flatten' => true,
                    'documents' => [[
                        'name' => 'levee-avertissement-'.$customer->getId().'.pdf',
                        'file' => 'data:application/pdf;base64,'.$pdfBase64,
                    ]],
                    'submitters' => [[
                        'email' => 'client@e-cosplay.fr',
                        'name' => 'Association E-Cosplay',
                        'role' => 'First Party',
                        'completed' => true,
                        'send_email' => false,
                        'values' => ['Sign' => $docuSeal->getLogoBase64()],
                        'metadata' => [
                            'doc_type' => 'client_warning_reset',
                            'customer_id' => $customer->getId(),
                        ],
                    ]],
                ]);

                @unlink($tmpPath);

                $this->addFlash('success', 'Avertissements reinitialises. Le client recevra le PDF signe de levee d\'avertissement.');
            } catch (\Throwable $e) {
                $this->addFlash('warning', 'Avertissements reinitialises mais erreur DocuSeal : '.$e->getMessage());
            }
        } else {
            $this->addFlash('success', 'Avertissements reinitialises.');
        }
        // @codeCoverageIgnoreEnd

        return $this->redirectToRoute('app_admin_clients_show', ['id' => $id, 'tab' => 'controle']);
    }
}
