<?php

declare(strict_types=1);

namespace MauticPlugin\MauticFocusBundle\Tests\EventListener;

use Mautic\CoreBundle\Test\MauticMysqlTestCase;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\PageBundle\Entity\Redirect;
use Mautic\PageBundle\Entity\Trackable;
use Mautic\ReportBundle\Entity\Report;
use MauticPlugin\MauticFocusBundle\Entity\Focus;
use MauticPlugin\MauticFocusBundle\Entity\Stat;
use MauticPlugin\MauticFocusBundle\EventListener\ReportSubscriber;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpFoundation\Request;

final class ReportSubscriberFunctionalTest extends MauticMysqlTestCase
{
    protected function setUp(): void
    {
        parent::setUp();
    }

    public function testGenerateFocusItemReportWithAllAvailableColumns(): void
    {
        $this->fillDatabase();

        $report = new Report();
        $report->setName('Focus Stats Report');
        $report->setSource(ReportSubscriber::CONTEXT_FOCUS_STATS);
        $report->setColumns([ReportSubscriber::PREFIX_FOCUS.'.name', ReportSubscriber::PREFIX_FOCUS.'.description', ReportSubscriber::PREFIX_FOCUS.'.focus_type', ReportSubscriber::PREFIX_FOCUS.'.style', ReportSubscriber::PREFIX_STATS.'.type', ReportSubscriber::PREFIX_TRACKABLES.'.hits', ReportSubscriber::PREFIX_TRACKABLES.'.unique_hits', ReportSubscriber::PREFIX_REDIRECTS.'.url',
        ]);
        $this->em->persist($report);
        $this->em->flush();

        $crawler      = $this->client->request(Request::METHOD_GET, "/s/reports/view/{$report->getId()}");
        $this->assertTrue($this->client->getResponse()->isOk());
        // get table with id=reportTable
        $crawlerTable = $crawler->filter('#reportTable');
        // remove first line of table (column names)
        $table        = array_slice($this->domTableToArray($crawlerTable), 1);
        // remove last line of table (unnecessary part generated by js)
        array_pop($table);

        $this->assertSame([
            ['1', 'FocusItem1', 'doesAbc', 'link', 'modal', 'click', '1', '1', 'http://example1.com'],
            ['2', 'FocusItem1', 'doesAbc', 'link', 'modal', 'view', '3', '2', 'http://example1.com'],
            ['3', 'FocusItem2', 'doesAbcd', 'link', 'modal', 'click', '1', '1', 'http://example2.com'],
            ['4', 'FocusItem2', 'doesAbcd', 'link', 'modal', 'view', '1', '1', 'http://example2.com'],
        ], $table);
    }

    public function testGenerateFocusItemReportFocusLeadsColumns(): void
    {
        $this->fillDatabase();

        $report = new Report();
        $report->setName('Focus Leads Report');
        $report->setSource(ReportSubscriber::CONTEXT_FOCUS_LEADS);
        $report->setColumns(
            [
                ReportSubscriber::PREFIX_LEADS.'.email',
                ReportSubscriber::PREFIX_FOCUS.'.name',
                ReportSubscriber::PREFIX_FOCUS.'.description',
                ReportSubscriber::PREFIX_FOCUS.'.focus_type',
                ReportSubscriber::PREFIX_FOCUS.'.style',
                ReportSubscriber::PREFIX_STATS.'.type',
                ReportSubscriber::PREFIX_TRACKABLES.'.hits',
                ReportSubscriber::PREFIX_REDIRECTS.'.url',
            ]
        );
        $this->em->persist($report);
        $this->em->flush();

        $crawler      = $this->client->request(Request::METHOD_GET, "/s/reports/view/{$report->getId()}");
        $this->assertTrue($this->client->getResponse()->isOk());

        // get table with id=reportTable
        $crawlerTable = $crawler->filter('#reportTable');
        // remove first line of table (column names)
        $table        = array_slice($this->domTableToArray($crawlerTable), 1);
        // remove last line of table (unnecessary part generated by js)
        array_pop($table);

        $this->assertSame([
            ['1', 'lead.1@example.com', 'FocusItem1', 'doesAbc', 'link', 'modal', 'click', '1', 'http://example1.com'],
            ['2', 'lead.1@example.com', 'FocusItem1', 'doesAbc', 'link', 'modal', 'view', '2', 'http://example1.com'],
            ['3', 'lead.2@example.com', 'FocusItem1', 'doesAbc', 'link', 'modal', 'view', '1', 'http://example1.com'],
            ['4', 'lead.2@example.com', 'FocusItem2', 'doesAbcd', 'link', 'modal', 'click', '1', 'http://example2.com'],
            ['5', 'lead.2@example.com', 'FocusItem2', 'doesAbcd', 'link', 'modal', 'view', '1', 'http://example2.com'],
        ], $table);
    }

    private function fillDatabase(): void
    {
        $lead1= $this->createContact('lead.1@example.com');
        $lead2= $this->createContact('lead.2@example.com');

        $focus1 = $this->createFocusItem('FocusItem1', 'doesAbc', 'link', 'modal');
        $focus2 = $this->createFocusItem('FocusItem2', 'doesAbcd', 'link', 'modal');
        $focus3 = $this->createFocusItem('FocusItem3', 'doesAbcde', 'link', 'modal');
        $this->em->flush();

        $date = new \DateTime();
        $this->createFocusStats('click', $focus1, $lead1, $date);
        $this->createFocusStats('click', $focus2, $lead2, $date);
        $this->createFocusStats('view', $focus1, $lead1, $date);
        $this->createFocusStats('view', $focus1, $lead1, $date);
        $this->createFocusStats('view', $focus1, $lead2, $date);
        $this->createFocusStats('view', $focus2, $lead2, $date);

        /** @var int $focusId1 */
        $focusId1 = $focus1->getId();
        /** @var int $focusId2 */
        $focusId2 = $focus2->getId();
        $this->createTrackableAndRedirects('http://example1.com', $focusId1, 1, 1);
        $this->createTrackableAndRedirects('http://example2.com', $focusId2, 1, 1);
        $this->createTrackableAndRedirects('http://example2.com', $focusId2);
        $this->em->flush();
        $this->em->clear();
    }

    private function createContact(string $email): Lead
    {
        $contact = new Lead();
        $contact->setEmail($email);
        $this->em->persist($contact);

        return $contact;
    }

    private function createFocusItem(string $name, string $description, string $focusType, string $style): Focus
    {
        $focus = new Focus();
        $focus->setName($name);
        $focus->setDescription($description);
        $focus->setType($focusType);
        $focus->setStyle($style);
        $this->em->persist($focus);

        return $focus;
    }

    private function createFocusStats(string $type, Focus $focus, Lead $lead, \DateTime $dateAdded): void
    {
        $focusStats = new Stat();
        $focusStats->setType($type);
        $focusStats->setFocus($focus);
        $focusStats->setLead($lead);
        $focusStats->setDateAdded($dateAdded);
        $this->em->persist($focusStats);
    }

    private function createTrackableAndRedirects(string $url, int $channelId, int $hits = 0, int $uniqueHits = 0): void
    {
        $redirect = new Redirect();
        $redirect->setRedirectId(uniqid());
        $redirect->setUrl($url);
        $redirect->setHits($hits);
        $redirect->setUniqueHits($uniqueHits);
        $this->em->persist($redirect);

        $trackable = new Trackable();
        $trackable->setChannelId($channelId);
        $trackable->setChannel('focus');
        $trackable->setHits($hits);
        $trackable->setUniqueHits($uniqueHits);
        $trackable->setRedirect($redirect);
        $this->em->persist($trackable);
    }

    /**
     * @return array<int,array<int,mixed>>
     */
    private function domTableToArray(Crawler $crawler): array
    {
        return $crawler->filter('tr')->each(fn ($tr) => $tr->filter('td')->each(fn ($td) => trim($td->text())));
    }
}
