<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Controller\Admin;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Admin\SocialPublicationType;
use CpCreation\VitiCore\SocialPublishing\PublicationRequest;
use CpCreation\VitiCore\SocialPublishing\SocialPublishingHub;
use CpCreation\VitiCore\SocialPublishing\SocialPublishingHubException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Uid\Uuid;
use Symfony\Contracts\Translation\TranslatorInterface;

#[Route('/social-network')]
#[IsGranted(new Expression("is_granted('ROLE_ADMIN') or is_granted('ROLE_SOCIAL')"))]
final class SocialNetworkController extends AbstractController
{
    public function __construct(
        private readonly SocialPublishingHub $hub,
        private readonly TranslatorInterface $translator,
    ) {}

    #[Route('', name: 'admin_social', methods: ['GET'])]
    public function index(): Response
    {
        $channels = [];
        $publications = [];
        $available = $this->hub->isAvailable();
        if ($available) {
            try {
                $channels = $this->hub->channels();
                $publications = $this->hub->publications();
            } catch (SocialPublishingHubException) {
                $available = false;
            }
        }

        return $this->render('@CpCreationVitiCore/admin/social/index.html.twig', [
            'hub_available' => $available,
            'channels' => $channels,
            'publications' => $publications,
        ]);
    }

    #[Route('/connect/{provider}', name: 'admin_social_connect', methods: ['POST'], requirements: ['provider' => 'meta'])]
    public function connect(string $provider, Request $request): RedirectResponse
    {
        if (!$this->isCsrfTokenValid('social-connect-' . $provider, (string) $request->request->get('_token'))) {
            throw $this->createAccessDeniedException();
        }

        return $this->startConnection($provider);
    }

    #[Route('/callback', name: 'admin_social_callback', methods: ['GET'])]
    public function callback(Request $request): RedirectResponse
    {
        $result = $request->query->get('social');
        $this->addFlash(
            'connected' === $result ? 'alert-success' : 'alert-danger',
            $this->translator->trans(
                'connected' === $result
                    ? 'social_publishing.flash.connected'
                    : 'social_publishing.flash.connection_failed',
            ),
        );

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

    #[Route('/channels/{channelId}/disconnect', name: 'admin_social_disconnect', methods: ['POST'])]
    public function disconnect(string $channelId, Request $request): RedirectResponse
    {
        if (!$this->isCsrfTokenValid('social-disconnect-' . $channelId, (string) $request->request->get('_token'))) {
            throw $this->createAccessDeniedException();
        }

        try {
            $this->hub->disconnect($channelId);
            $this->addFlash('alert-success', $this->translator->trans('social_publishing.flash.disconnected'));
        } catch (SocialPublishingHubException) {
            $this->addFlash('alert-danger', $this->translator->trans('social_publishing.flash.hub_error'));
        }

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

    #[Route('/publications/new', name: 'admin_social_publication_new', methods: ['GET', 'POST'])]
    public function newPublication(Request $request): Response
    {
        try {
            $channels = $this->hub->channels();
        } catch (SocialPublishingHubException) {
            $this->addFlash('alert-danger', $this->translator->trans('social_publishing.flash.hub_error'));

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

        $choices = [];
        $channelsById = [];
        foreach ($channels as $channel) {
            $choices[$channel->name . ' · ' . $channel->type] = $channel->id;
            $channelsById[$channel->id] = $channel;
        }
        $form = $this->createForm(SocialPublicationType::class, [
            'scheduledAt' => new \DateTimeImmutable('+5 minutes'),
        ], [
            'channel_choices' => $choices,
        ]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $message = \is_string($data['message'] ?? null) ? trim($data['message']) : '';
            $mediaUrl = \is_string($data['mediaUrl'] ?? null) ? trim($data['mediaUrl']) : null;
            $canPublish = true;
            if ('' === $message && (null === $mediaUrl || '' === $mediaUrl)) {
                $canPublish = false;
                $form->get('message')->addError(new FormError(
                    $this->translator->trans('social_publishing.form.content_required'),
                ));
            } else {
                $selectedChannels = \is_array($data['channels'] ?? null) ? $data['channels'] : [];
                foreach ($selectedChannels as $channelId) {
                    $channel = \is_string($channelId) ? ($channelsById[$channelId] ?? null) : null;
                    if (null === $channel) {
                        continue;
                    }
                    $contentTypes = \is_array($channel->capabilities['content_types'] ?? null)
                        ? $channel->capabilities['content_types']
                        : [];
                    if (
                        ((null === $mediaUrl || '' === $mediaUrl) && !\in_array('text', $contentTypes, true))
                        || (null !== $mediaUrl && '' !== $mediaUrl && !\in_array('image', $contentTypes, true))
                    ) {
                        $canPublish = false;
                        $form->get('channels')->addError(new FormError(
                            $this->translator->trans('social_publishing.form.capability_mismatch', [
                                '%channel%' => $channel->name,
                            ]),
                        ));
                        break;
                    }
                    $maximumLength = $channel->capabilities['max_text_length'] ?? null;
                    if (\is_int($maximumLength) && mb_strlen($message) > $maximumLength) {
                        $canPublish = false;
                        $form->get('message')->addError(new FormError(
                            $this->translator->trans('social_publishing.form.message_too_long', [
                                '%channel%' => $channel->name,
                                '%limit%' => $maximumLength,
                            ]),
                        ));
                        break;
                    }
                }
            }

            if ($canPublish) {
                try {
                    $this->hub->publish(new PublicationRequest(
                        Uuid::v7()->toRfc4122(),
                        $message,
                        '' === $mediaUrl ? null : $mediaUrl,
                        $data['scheduledAt'],
                        array_values($data['channels']),
                    ));
                    $this->addFlash('alert-success', $this->translator->trans('social_publishing.flash.scheduled'));

                    return $this->redirectToRoute('admin_social');
                } catch (SocialPublishingHubException) {
                    $form->addError(new FormError($this->translator->trans('social_publishing.flash.hub_error')));
                }
            }
        }

        return $this->render('@CpCreationVitiCore/admin/social/new.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    #[Route('/publications/{publicationId}/cancel', name: 'admin_social_publication_cancel', methods: ['POST'])]
    public function cancelPublication(string $publicationId, Request $request): RedirectResponse
    {
        if (!$this->isCsrfTokenValid('social-cancel-' . $publicationId, (string) $request->request->get('_token'))) {
            throw $this->createAccessDeniedException();
        }

        try {
            $this->hub->cancel($publicationId);
            $this->addFlash('alert-success', $this->translator->trans('social_publishing.flash.cancelled'));
        } catch (SocialPublishingHubException) {
            $this->addFlash('alert-danger', $this->translator->trans('social_publishing.flash.hub_error'));
        }

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

    #[Route('/instagram/login', name: 'admin_social_instagram_login', methods: ['GET'])]
    public function legacyInstagramLogin(): RedirectResponse
    {
        return $this->startConnection('meta');
    }

    private function startConnection(string $provider): RedirectResponse
    {
        try {
            $returnUrl = $this->generateUrl(
                'admin_social_callback',
                referenceType: UrlGeneratorInterface::ABSOLUTE_URL,
            );

            return new RedirectResponse($this->hub->startConnection($provider, $returnUrl));
        } catch (SocialPublishingHubException) {
            $this->addFlash('alert-danger', $this->translator->trans('social_publishing.flash.hub_error'));

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