<?php

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

use CpCreation\VitiCore\Marketing\Emailing\BrevoApiException;
use CpCreation\VitiCore\Marketing\Emailing\BrevoClient;
use CpCreation\VitiCore\Marketing\Emailing\BrevoSecretCipher;
use CpCreation\VitiCore\Marketing\Emailing\UtmLinkEnricher;
use CpCreation\VitiCore\Marketing\Emailing\VitiCoreRecipientProvider;
use CpCreation\VitiCore\Marketing\Model\EmailCampaign;
use CpCreation\VitiCore\Marketing\Repository\BrevoConfigurationRepository;
use CpCreation\VitiCore\Marketing\Repository\EmailCampaignRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface;

#[Route('/marketing/emailing')]
#[IsGranted(new Expression("is_granted('ROLE_ADMIN') or is_granted('ROLE_MARKETING')"))]
final class EmailingController extends AbstractController
{
    public function __construct(
        private readonly BrevoConfigurationRepository $configurationRepository,
        private readonly EmailCampaignRepository $campaignRepository,
        private readonly BrevoClient $brevoClient,
        private readonly BrevoSecretCipher $secretCipher,
        private readonly VitiCoreRecipientProvider $recipientProvider,
        private readonly UtmLinkEnricher $utmLinkEnricher,
        private readonly TranslatorInterface $translator,
    ) {
    }

    #[Route('', name: 'admin_marketing_emailing', methods: ['GET'])]
    public function index(): Response
    {
        return $this->render('@CpCreationVitiCore/admin/marketing/emailing/index.html.twig', [
            'configuration' => $this->configurationRepository->get(),
            'campaigns' => $this->campaignRepository->findRecent(),
        ]);
    }

    #[Route('/configuration', name: 'admin_marketing_emailing_configuration', methods: ['GET', 'POST'])]
    #[IsGranted('ROLE_SUPER_ADMIN')]
    public function configuration(Request $request): Response
    {
        $configuration = $this->configurationRepository->getOrCreate();
        if ($request->isMethod('POST')) {
            $this->assertCsrf('brevo_configuration', $request);
            $enabled = $request->request->getBoolean('enabled');
            $apiKey = mb_trim($request->request->getString('api_key'));
            if ('' !== $apiKey) {
                $configuration->replaceApiKey($apiKey, $this->secretCipher);
            }
            $configuration->setEnabled($enabled);
            if ($enabled && !$configuration->hasApiKey()) {
                $this->addFlash('alert-danger', $this->translator->trans('emailing.configuration.api_key_required'));
            } else {
                $this->configurationRepository->save($configuration);
                if ($enabled) {
                    try {
                        $this->brevoClient->account();
                        $this->addFlash('alert-success', $this->translator->trans('emailing.configuration.connection_ok'));
                    } catch (BrevoApiException $exception) {
                        $this->addFlash('alert-danger', $exception->getMessage());
                    }
                } else {
                    $this->addFlash('alert-success', $this->translator->trans('emailing.configuration.saved'));
                }

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

        return $this->render('@CpCreationVitiCore/admin/marketing/emailing/configuration.html.twig', [
            'configuration' => $configuration,
        ]);
    }

    #[Route('/domaines', name: 'admin_marketing_emailing_domains', methods: ['GET'])]
    #[IsGranted('ROLE_SUPER_ADMIN')]
    public function domains(): Response
    {
        $domains = [];
        try {
            $payload = $this->brevoClient->domains();
            foreach (\array_slice($payload['domains'] ?? [], 0, 100) as $domain) {
                if (!\is_array($domain)) {
                    continue;
                }
                $name = $domain['domain_name'] ?? $domain['domain'] ?? null;
                if (!\is_string($name) || '' === $name) {
                    continue;
                }
                try {
                    $domains[] = $this->brevoClient->domain($name);
                } catch (BrevoApiException) {
                    $domains[] = $domain;
                }
            }
        } catch (BrevoApiException $exception) {
            $this->addFlash('alert-danger', $exception->getMessage());
        }

        return $this->render('@CpCreationVitiCore/admin/marketing/emailing/domains.html.twig', [
            'domains' => $domains,
        ]);
    }

    #[Route('/domaines/creer', name: 'admin_marketing_emailing_domain_create', methods: ['POST'])]
    #[IsGranted('ROLE_SUPER_ADMIN')]
    public function createDomain(Request $request): Response
    {
        $this->assertCsrf('brevo_domain_create', $request);
        $domain = mb_strtolower(mb_trim($request->request->getString('domain')));
        if (1 !== preg_match('/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/D', $domain)) {
            $this->addFlash('alert-danger', $this->translator->trans('emailing.domain.invalid'));
        } else {
            try {
                $this->brevoClient->createDomain($domain);
                $this->addFlash('alert-success', $this->translator->trans('emailing.domain.created'));
            } catch (BrevoApiException $exception) {
                $this->addFlash('alert-danger', $exception->getMessage());
            }
        }

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

    #[Route('/domaines/{domain}/verifier', name: 'admin_marketing_emailing_domain_authenticate', methods: ['POST'])]
    #[IsGranted('ROLE_SUPER_ADMIN')]
    public function authenticateDomain(string $domain, Request $request): Response
    {
        $this->assertCsrf('brevo_domain_authenticate_'.$domain, $request);
        try {
            $this->brevoClient->authenticateDomain($domain);
            $this->addFlash('alert-success', $this->translator->trans('emailing.domain.checked'));
        } catch (BrevoApiException $exception) {
            $this->addFlash('alert-danger', $exception->getMessage());
        }

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

    #[Route('/listes', name: 'admin_marketing_emailing_lists', methods: ['GET'])]
    public function lists(): Response
    {
        $lists = [];
        $folders = [];
        try {
            $lists = $this->brevoClient->lists()['lists'] ?? [];
            $folders = $this->brevoClient->folders()['folders'] ?? [];
        } catch (BrevoApiException $exception) {
            $this->addFlash('alert-danger', $exception->getMessage());
        }

        return $this->render('@CpCreationVitiCore/admin/marketing/emailing/lists.html.twig', [
            'lists' => $lists,
            'folders' => $folders,
        ]);
    }

    #[Route('/listes/creer', name: 'admin_marketing_emailing_list_create', methods: ['POST'])]
    public function createList(Request $request): Response
    {
        $this->assertCsrf('brevo_list_create', $request);
        $name = mb_trim($request->request->getString('name'));
        $folderId = $request->request->getInt('folder_id');
        if ('' === $name || $folderId <= 0) {
            $this->addFlash('alert-danger', $this->translator->trans('emailing.list.invalid'));
        } else {
            try {
                $this->brevoClient->createList($name, $folderId);
                $this->addFlash('alert-success', $this->translator->trans('emailing.list.created'));
            } catch (BrevoApiException $exception) {
                $this->addFlash('alert-danger', $exception->getMessage());
            }
        }

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

    #[Route('/listes/{listId}/renommer', name: 'admin_marketing_emailing_list_update', requirements: ['listId' => '\d+'], methods: ['POST'])]
    public function updateList(int $listId, Request $request): Response
    {
        $this->assertCsrf('brevo_list_update_' . $listId, $request);
        $name = mb_trim($request->request->getString('name'));
        if ('' === $name) {
            $this->addFlash('alert-danger', $this->translator->trans('emailing.list.invalid'));
        } else {
            try {
                $this->brevoClient->updateList($listId, $name);
                $this->addFlash('alert-success', $this->translator->trans('emailing.list.updated'));
            } catch (BrevoApiException $exception) {
                $this->addFlash('alert-danger', $exception->getMessage());
            }
        }

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

    #[Route('/listes/{listId}/supprimer', name: 'admin_marketing_emailing_list_delete', requirements: ['listId' => '\d+'], methods: ['POST'])]
    public function deleteList(int $listId, Request $request): Response
    {
        $this->assertCsrf('brevo_list_delete_' . $listId, $request);
        try {
            $this->brevoClient->deleteList($listId);
            $this->addFlash('alert-success', $this->translator->trans('emailing.list.deleted'));
        } catch (BrevoApiException $exception) {
            $this->addFlash('alert-danger', $exception->getMessage());
        }

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

    #[Route('/listes/importer', name: 'admin_marketing_emailing_list_import', methods: ['POST'])]
    public function importList(Request $request): Response
    {
        $this->assertCsrf('brevo_list_import', $request);
        $listId = $request->request->getInt('list_id');
        $file = $request->files->get('contacts_file');
        if ($listId <= 0 || !$file instanceof UploadedFile || !$file->isValid() || $file->getSize() > 8_000_000) {
            $this->addFlash('alert-danger', $this->translator->trans('emailing.list.import_invalid'));
        } else {
            try {
                $this->brevoClient->importCsv($listId, $file->getContent());
                $this->addFlash('alert-success', $this->translator->trans('emailing.list.import_queued'));
            } catch (BrevoApiException $exception) {
                $this->addFlash('alert-danger', $exception->getMessage());
            }
        }

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

    #[Route('/listes/synchroniser', name: 'admin_marketing_emailing_list_sync', methods: ['POST'])]
    public function syncList(Request $request): Response
    {
        $this->assertCsrf('brevo_list_sync', $request);
        $listId = $request->request->getInt('list_id');
        $source = $request->request->getString('source');
        try {
            $contacts = $this->recipientProvider->contacts($source);
            if ($listId <= 0 || [] === $contacts) {
                throw new \InvalidArgumentException();
            }
            foreach (array_chunk($contacts, 5000) as $chunk) {
                $this->brevoClient->importContacts($listId, $chunk);
            }
            $this->addFlash('alert-success', $this->translator->trans('emailing.list.sync_queued', [
                '%count%' => \count($contacts),
            ]));
        } catch (\InvalidArgumentException) {
            $this->addFlash('alert-danger', $this->translator->trans('emailing.list.sync_invalid'));
        } catch (BrevoApiException $exception) {
            $this->addFlash('alert-danger', $exception->getMessage());
        }

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

    #[Route('/campagnes/nouvelle', name: 'admin_marketing_emailing_campaign_new', methods: ['GET', 'POST'])]
    public function newCampaign(Request $request): Response
    {
        $senders = [];
        $lists = [];
        try {
            $senders = $this->brevoClient->senders()['senders'] ?? [];
            $lists = $this->brevoClient->lists()['lists'] ?? [];
        } catch (BrevoApiException $exception) {
            $this->addFlash('alert-danger', $exception->getMessage());
        }

        if ($request->isMethod('POST')) {
            $this->assertCsrf('brevo_campaign_create', $request);
            $title = mb_trim($request->request->getString('title'));
            $subject = mb_trim($request->request->getString('subject'));
            $content = mb_trim($request->request->getString('text_content'));
            $senderId = $request->request->getInt('sender_id');
            $listIds = array_values(array_filter(array_map(
                'intval',
                (array) $request->request->all('list_ids'),
            ), static fn (int $id): bool => $id > 0));
            $utmSource = mb_trim($request->request->getString('utm_source', $title));
            if ('' === $utmSource) {
                $utmSource = $title;
            }
            if ('' === $title || '' === $subject || mb_strlen($content) < 10 || $senderId <= 0 || [] === $listIds) {
                $this->addFlash('alert-danger', $this->translator->trans('emailing.campaign.invalid'));
            } else {
                $campaign = new EmailCampaign(
                    $title,
                    $subject,
                    $content,
                    $senderId,
                    $listIds,
                    $utmSource,
                    $request->request->getString('utm_medium', 'email'),
                    '' !== mb_trim($request->request->getString('utm_campaign'))
                        ? $request->request->getString('utm_campaign')
                        : $title,
                    $request->request->getString('utm_term'),
                    $request->request->getString('utm_content'),
                );
                $this->campaignRepository->save($campaign);
                try {
                    $result = $this->brevoClient->createCampaign([
                        'name' => $campaign->getTitle(),
                        'subject' => $campaign->getSubject(),
                        'sender' => ['id' => $campaign->getSenderId()],
                        'recipients' => ['listIds' => $campaign->getRecipientListIds()],
                        'htmlContent' => $this->utmLinkEnricher->enrichPlainText($campaign),
                        'footer' => '[DEFAULT_FOOTER]',
                        'utmCampaign' => $campaign->getUtmCampaign(),
                    ]);
                    if (!\is_int($result['id'] ?? null)) {
                        throw new BrevoApiException('Brevo n’a pas retourné l’identifiant de la campagne.');
                    }
                    $campaign->markCreated($result['id']);
                    $this->campaignRepository->save($campaign);
                    $this->addFlash('alert-success', $this->translator->trans('emailing.campaign.created'));

                    return $this->redirectToRoute('admin_marketing_emailing');
                } catch (BrevoApiException $exception) {
                    $campaign->markFailure($exception->getMessage());
                    $this->campaignRepository->save($campaign);
                    $this->addFlash('alert-danger', $exception->getMessage());
                }
            }
        }

        return $this->render('@CpCreationVitiCore/admin/marketing/emailing/campaign_form.html.twig', [
            'senders' => $senders,
            'lists' => $lists,
        ]);
    }

    #[Route('/campagnes/{id}/envoyer', name: 'admin_marketing_emailing_campaign_send', methods: ['POST'])]
    public function sendCampaign(EmailCampaign $campaign, Request $request): Response
    {
        $this->assertCsrf('brevo_campaign_send_'.$campaign->getId()->toString(), $request);
        if (null === $campaign->getBrevoCampaignId() || 'sent' === $campaign->getStatus()) {
            $this->addFlash('alert-danger', $this->translator->trans('emailing.campaign.not_sendable'));
        } else {
            try {
                $this->brevoClient->sendCampaignNow($campaign->getBrevoCampaignId());
                $campaign->markSent();
                $this->campaignRepository->save($campaign);
                $this->addFlash('alert-success', $this->translator->trans('emailing.campaign.sent'));
            } catch (BrevoApiException $exception) {
                $campaign->markFailure($exception->getMessage());
                $this->campaignRepository->save($campaign);
                $this->addFlash('alert-danger', $exception->getMessage());
            }
        }

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

    private function assertCsrf(string $id, Request $request): void
    {
        if (!$this->isCsrfTokenValid($id, $request->request->getString('_token'))) {
            throw $this->createAccessDeniedException($this->translator->trans('emailing.common.invalid_csrf'));
        }
    }
}
