<?php

namespace App\Controller;

use App\Entity\AnalyticsEvent;
use App\Entity\AnalyticsUniqId;
use App\Entity\AuditLog;
use App\Entity\BilletBuyer;
use App\Entity\BilletOrder;
use App\Entity\Event;
use App\Service\AuditService;
use App\Service\BilletOrderService;
use App\Service\EventIndexService;
use App\Service\ExportService;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/admin')]
#[IsGranted('ROLE_ROOT')]
class AdminOrdersController extends AbstractController
{
    private const DQL_STATUS_PAID = 'o.status = :paid';

    #[Route('/commandes', name: 'app_admin_orders', methods: ['GET'])]
    public function orders(Request $request, EntityManagerInterface $em, PaginatorInterface $paginator): Response
    {
        $status = $request->query->getString('status', '');
        $search = $request->query->getString('q', '');

        $qb = $em->createQueryBuilder()
            ->select('o', 'i')
            ->from(BilletBuyer::class, 'o')
            ->leftJoin('o.items', 'i')
            ->orderBy('o.createdAt', 'DESC');

        if ('' !== $status) {
            $qb->andWhere('o.status = :status')->setParameter('status', $status);
        }

        if ('' !== $search) {
            $qb->andWhere('o.orderNumber LIKE :q OR o.firstName LIKE :q OR o.lastName LIKE :q OR o.email LIKE :q')
                ->setParameter('q', '%'.$search.'%');
        }

        $orders = $paginator->paginate($qb->getQuery(), $request->query->getInt('page', 1), 20);

        $totalCA = $em->createQueryBuilder()
            ->select('SUM(o.totalHT)')
            ->from(BilletBuyer::class, 'o')
            ->where(self::DQL_STATUS_PAID)
            ->setParameter('paid', BilletBuyer::STATUS_PAID)
            ->getQuery()
            ->getSingleScalarResult() ?? 0;

        $totalOrders = $em->getRepository(BilletBuyer::class)->count(['status' => BilletBuyer::STATUS_PAID]);
        $totalRefunded = $em->getRepository(BilletBuyer::class)->count(['status' => BilletBuyer::STATUS_REFUNDED]);
        $totalCancelled = $em->getRepository(BilletBuyer::class)->count(['status' => BilletBuyer::STATUS_CANCELLED]);
        $totalPending = $em->getRepository(BilletBuyer::class)->count(['status' => BilletBuyer::STATUS_PENDING]);

        return $this->render('admin/orders.html.twig', [
            'orders' => $orders,
            'status' => $status,
            'search' => $search,
            'totalCA' => (int) $totalCA / 100,
            'totalOrders' => $totalOrders,
            'totalRefunded' => $totalRefunded,
            'totalCancelled' => $totalCancelled,
            'totalPending' => $totalPending,
        ]);
    }

    #[Route('/commandes/{id}/forcer-validation', name: 'app_admin_order_force_validate', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function forceValidateOrder(int $id, EntityManagerInterface $em, BilletOrderService $billetOrderService): Response
    {
        $order = $em->getRepository(BilletBuyer::class)->find($id);
        if (!$order) {
            throw $this->createNotFoundException();
        }

        if (BilletBuyer::STATUS_PENDING !== $order->getStatus()) {
            $this->addFlash('error', 'Seules les commandes en attente peuvent etre forcees.');

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

        $billetOrderService->generateOrderTickets($order);
        $billetOrderService->generateAndSendTickets($order);
        $billetOrderService->notifyOrganizer($order);

        $this->addFlash('success', 'Commande '.$order->getOrderNumber().' validee avec succes. Billets generes et envoyes.');

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

    #[Route('/commandes/{id}/billets', name: 'app_admin_order_tickets', requirements: ['id' => '\d+'], methods: ['GET'])]
    public function orderTickets(int $id, EntityManagerInterface $em, BilletOrderService $billetOrderService): Response
    {
        $order = $em->getRepository(BilletBuyer::class)->find($id);
        if (!$order) {
            throw $this->createNotFoundException();
        }

        $tickets = $em->getRepository(BilletOrder::class)->findBy(['billetBuyer' => $order]);
        if (!$tickets) {
            throw $this->createNotFoundException('Aucun billet pour cette commande.');
        }

        if (1 === \count($tickets)) {
            $pdf = $billetOrderService->generatePdf($tickets[0]);

            return new Response($pdf, 200, [
                'Content-Type' => 'application/pdf',
                'Content-Disposition' => 'inline; filename="'.$tickets[0]->getReference().'.pdf"',
            ]);
        }

        $zip = new \ZipArchive();
        $tmpFile = tempnam(sys_get_temp_dir(), 'tickets_');
        $zip->open($tmpFile, \ZipArchive::OVERWRITE);

        foreach ($tickets as $ticket) {
            $pdf = $billetOrderService->generatePdf($ticket);
            $zip->addFromString($ticket->getReference().'.pdf', $pdf);
        }

        $zip->close();
        $content = file_get_contents($tmpFile);
        unlink($tmpFile);

        return new Response($content, 200, [
            'Content-Type' => 'application/zip',
            'Content-Disposition' => 'attachment; filename="billets_'.$order->getOrderNumber().'.zip"',
        ]);
    }

    #[Route('/evenements', name: 'app_admin_events')]
    public function events(Request $request, PaginatorInterface $paginator, EventIndexService $eventIndex): Response
    {
        $searchQuery = $request->query->getString('q', '');
        $eventsQuery = $eventIndex->searchEvents('event_admin', $searchQuery);

        $events = $paginator->paginate($eventsQuery, $request->query->getInt('page', 1), 10);

        return $this->render('admin/events.html.twig', [
            'events' => $events,
            'searchQuery' => $searchQuery,
        ]);
    }

    #[Route('/evenement/{id}/en-ligne', name: 'app_admin_toggle_event_online', methods: ['POST'])]
    public function toggleEventOnline(Event $event, EntityManagerInterface $em, EventIndexService $eventIndex): Response
    {
        $event->setIsOnline(!$event->isOnline());
        $em->flush();

        $eventIndex->indexEvent($event);

        $this->addFlash('success', $event->isOnline() ? 'Evenement mis en ligne.' : 'Evenement passe hors ligne.');

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

    #[Route('/evenement/{id}/supprimer', name: 'app_admin_delete_event', methods: ['POST'])]
    public function adminDeleteEvent(Event $event, EntityManagerInterface $em, EventIndexService $eventIndex, AuditService $audit): Response
    {
        $eventTitle = $event->getTitle();
        $eventDbId = $event->getId();
        $eventIndex->removeEvent($event);

        $em->remove($event);
        $em->flush();

        $audit->log('event_deleted', 'Event', $eventDbId, ['title' => $eventTitle]);

        $this->addFlash('success', 'Evenement supprime.');

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

    #[Route('/export/{year}/{month}', name: 'app_admin_export', requirements: ['year' => '\d{4}', 'month' => '\d{1,2}'], methods: ['GET'])]
    public function export(int $year, int $month, ExportService $exportService): Response
    {
        $stats = $exportService->getMonthlyStats($year, $month);
        $csv = $exportService->generateCsv($stats['orders'], true);

        $filename = sprintf('export_admin_%04d_%02d.csv', $year, $month);

        return new Response($csv, 200, [
            'Content-Type' => 'text/csv; charset=utf-8',
            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
        ]);
    }

    #[Route('/export/{year}/{month}/pdf', name: 'app_admin_export_pdf', requirements: ['year' => '\d{4}', 'month' => '\d{1,2}'], methods: ['GET'])]
    public function exportPdf(int $year, int $month, ExportService $exportService): Response
    {
        $stats = $exportService->getMonthlyStats($year, $month);
        $pdf = $exportService->generatePdf($stats, $year, $month);

        return new Response($pdf, 200, [
            'Content-Type' => 'application/pdf',
            'Content-Disposition' => 'inline; filename="recap_admin_'.sprintf('%04d_%02d', $year, $month).'.pdf"',
        ]);
    }

    #[Route('/logs', name: 'app_admin_logs', methods: ['GET'])]
    public function logs(Request $request, EntityManagerInterface $em, PaginatorInterface $paginator): Response
    {
        $logs = $em->getRepository(AuditLog::class)->findBy([], ['createdAt' => 'DESC']);
        $paginatedLogs = $paginator->paginate($logs, $request->query->getInt('page', 1), 30);

        return $this->render('admin/logs.html.twig', [
            'logs' => $paginatedLogs,
        ]);
    }

    #[Route('/analytics', name: 'app_admin_analytics', methods: ['GET'])]
    public function analytics(Request $request, EntityManagerInterface $em): Response
    {
        $period = $request->query->getString('period', '7d');
        $since = match ($period) {
            'today' => new \DateTimeImmutable('today'),
            '30d' => new \DateTimeImmutable('-30 days'),
            'all' => new \DateTimeImmutable('2020-01-01'),
            default => new \DateTimeImmutable('-7 days'),
        };

        $visitors = (int) $em->createQueryBuilder()
            ->select('COUNT(v.id)')
            ->from(AnalyticsUniqId::class, 'v')
            ->where('v.createdAt >= :since')
            ->setParameter('since', $since)
            ->getQuery()
            ->getSingleScalarResult();

        $pageviews = (int) $em->createQueryBuilder()
            ->select('COUNT(e.id)')
            ->from(AnalyticsEvent::class, 'e')
            ->where('e.createdAt >= :since')
            ->setParameter('since', $since)
            ->getQuery()
            ->getSingleScalarResult();

        // Bounce rate: visitors with only 1 pageview
        $bouncedVisitors = (int) $em->createQueryBuilder()
            ->select('COUNT(v.id)')
            ->from(AnalyticsUniqId::class, 'v')
            ->where('v.createdAt >= :since')
            ->andWhere('(SELECT COUNT(e2.id) FROM '.AnalyticsEvent::class.' e2 WHERE e2.visitor = v.id) = 1')
            ->setParameter('since', $since)
            ->getQuery()
            ->getSingleScalarResult();

        $bounceRate = $visitors > 0 ? round($bouncedVisitors / $visitors * 100, 1) : 0;

        $topPages = $em->createQueryBuilder()
            ->select('e.url, COUNT(e.id) AS hits')
            ->from(AnalyticsEvent::class, 'e')
            ->where('e.createdAt >= :since')
            ->setParameter('since', $since)
            ->groupBy('e.url')
            ->orderBy('hits', 'DESC')
            ->setMaxResults(20)
            ->getQuery()
            ->getArrayResult();

        $topReferrers = $em->createQueryBuilder()
            ->select('e.referrer, COUNT(e.id) AS hits')
            ->from(AnalyticsEvent::class, 'e')
            ->where('e.createdAt >= :since')
            ->andWhere('e.referrer IS NOT NULL')
            ->andWhere("e.referrer != ''")
            ->andWhere('e.referrer NOT LIKE :self1')
            ->andWhere('e.referrer NOT LIKE :self2')
            ->setParameter('self1', '%ticket.e-cosplay.fr%')
            ->setParameter('self2', '%esyweb.local%')
            ->setParameter('since', $since)
            ->groupBy('e.referrer')
            ->orderBy('hits', 'DESC')
            ->setMaxResults(10)
            ->getQuery()
            ->getArrayResult();

        $devices = $em->createQueryBuilder()
            ->select('v.deviceType, COUNT(v.id) AS cnt')
            ->from(AnalyticsUniqId::class, 'v')
            ->where('v.createdAt >= :since')
            ->setParameter('since', $since)
            ->groupBy('v.deviceType')
            ->getQuery()
            ->getArrayResult();

        $browsers = $em->createQueryBuilder()
            ->select('v.browser, COUNT(v.id) AS cnt')
            ->from(AnalyticsUniqId::class, 'v')
            ->where('v.createdAt >= :since')
            ->andWhere('v.browser IS NOT NULL')
            ->setParameter('since', $since)
            ->groupBy('v.browser')
            ->orderBy('cnt', 'DESC')
            ->setMaxResults(10)
            ->getQuery()
            ->getArrayResult();

        $osList = $em->createQueryBuilder()
            ->select('v.os, COUNT(v.id) AS cnt')
            ->from(AnalyticsUniqId::class, 'v')
            ->where('v.createdAt >= :since')
            ->andWhere('v.os IS NOT NULL')
            ->setParameter('since', $since)
            ->groupBy('v.os')
            ->orderBy('cnt', 'DESC')
            ->getQuery()
            ->getArrayResult();

        // Daily chart data
        $conn = $em->getConnection();

        $visitorsPerDay = $conn->executeQuery(
            'SELECT CAST(created_at AS DATE) AS day, COUNT(*) AS cnt FROM analytics_uniq_id WHERE created_at >= :since GROUP BY day ORDER BY day ASC',
            ['since' => $since->format('Y-m-d H:i:s')],
        )->fetchAllAssociative();

        $pageviewsPerDay = $conn->executeQuery(
            'SELECT CAST(created_at AS DATE) AS day, COUNT(*) AS cnt FROM analytics_event WHERE created_at >= :since GROUP BY day ORDER BY day ASC',
            ['since' => $since->format('Y-m-d H:i:s')],
        )->fetchAllAssociative();

        // Merge into aligned arrays
        $allDays = [];
        foreach ($visitorsPerDay as $r) {
            $allDays[$r['day']] = true;
        }
        foreach ($pageviewsPerDay as $r) {
            $allDays[$r['day']] = true;
        }
        ksort($allDays);

        $visitorsMap = [];
        foreach ($visitorsPerDay as $r) {
            $d = $r['day'] instanceof \DateTimeInterface ? $r['day']->format('Y-m-d') : (string) $r['day'];
            $visitorsMap[$d] = (int) $r['cnt'];
        }
        $pageviewsMap = [];
        foreach ($pageviewsPerDay as $r) {
            $d = $r['day'] instanceof \DateTimeInterface ? $r['day']->format('Y-m-d') : (string) $r['day'];
            $pageviewsMap[$d] = (int) $r['cnt'];
        }

        $chartLabels = array_keys($allDays);
        $chartVisitors = array_map(fn ($d) => (int) ($visitorsMap[$d] ?? 0), $chartLabels);
        $chartPageviews = array_map(fn ($d) => (int) ($pageviewsMap[$d] ?? 0), $chartLabels);

        return $this->render('admin/analytics.html.twig', [
            'period' => $period,
            'visitors' => $visitors,
            'pageviews' => $pageviews,
            'bounce_rate' => $bounceRate,
            'top_pages' => $topPages,
            'top_referrers' => $topReferrers,
            'devices' => $devices,
            'browsers' => $browsers,
            'os_list' => $osList,
            'chart_labels' => $chartLabels,
            'chart_visitors' => $chartVisitors,
            'chart_pageviews' => $chartPageviews,
        ]);
    }
}
