<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */

namespace Piwik\Plugins\PrivacyManager\tests\System;

use Piwik\API\Request;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
use Piwik\Plugins\PrivacyManager\ReportsPurger;
use Piwik\Tests\Framework\TestCase\SystemTestCase;
use Piwik\Tests\Fixtures\OneVisitorTwoVisits;

/**
 * @group PrivacyManager
 * @group Core
 * @group PurgeDataTest
 */
class PurgeDataTest extends SystemTestCase
{
    public static $fixture = null; // initialized below class definition

    public static function setUpBeforeClass(): void
    {
    }
    public static function tearDownBeforeClass()
    {
    }

    public function setUp(): void
    {
        parent::setUpBeforeClass();
    }

    public function tearDown(): void
    {
        parent::tearDownAfterClass();
    }

    public function testPurgeDataKeepAllExceptDay()
    {
        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');

        $deleteReportsOlderThan = 1;
        $keepBasicMetrics       = true;
        $reportPeriodsToKeep    = array(2,3,4,5);
        $this->purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics);

        $this->assertHasNoDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');
    }

    public function testPurgeDataKeepOnlyDay()
    {
        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');

        $deleteReportsOlderThan = 1;
        $keepBasicMetrics       = true;
        $reportPeriodsToKeep    = array(1);
        $this->purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics);

        $this->assertNumVisits(2, 'day');
        $this->assertNumVisits(2, 'week');
        $this->assertHasOneDownload('day');
        $this->assertHasNoDownload('week');
        $this->assertHasNoDownload('month');
        $this->assertHasNoDownload('year');
    }

    public function testPurgeDataShouldNotPurgeAnythingIfDeleteReportsOlderThanIsFarBackInThePast()
    {
        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');

        $deleteReportsOlderThan = 1000;
        $keepBasicMetrics       = true;
        $reportPeriodsToKeep    = array(1,2,3,4,5);
        $this->purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics);

        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');
    }

    public function testPurgeDataShouldPurgeAllPeriodsExceptBasicMetricsIfNoPeriodToKeepIsGiven()
    {
        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');

        $deleteReportsOlderThan = 1;
        $keepBasicMetrics       = true;
        $reportPeriodsToKeep    = array();
        $this->purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics);

        $this->assertNumVisits(2, 'day');
        $this->assertNumVisits(2, 'week');
        $this->assertNumVisits(2, 'month');
        $this->assertNumVisits(2, 'year');
        $this->assertHasNoDownload('day');
        $this->assertHasNoDownload('week');
        $this->assertHasNoDownload('month');
        $this->assertHasNoDownload('year');
    }

    public function testPurgeDataShouldPurgeEverythingIfNoPeriodToKeepIsGivenAndBasicMetricsNotKept()
    {
        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');

        $deleteReportsOlderThan = 1;
        $keepBasicMetrics       = false;
        $reportPeriodsToKeep    = array();
        $this->purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics);

        $this->assertNumVisits(0, 'day');
        $this->assertNumVisits(0, 'week');
        $this->assertNumVisits(0, 'month');
        $this->assertNumVisits(0, 'year');
        $this->assertHasNoDownload('day');
        $this->assertHasNoDownload('week');
        $this->assertHasNoDownload('month');
        $this->assertHasNoDownload('year');
    }

    public function testPurgeDataShouldPurgeEverythingIfNoPeriodToKeepIsGivenAndBasicMetricsNotKeptSegmentsKept()
    {
        $this->assertHasOneDownload('day');
        $this->assertHasOneDownload('week');
        $this->assertHasOneDownload('month');
        $this->assertHasOneDownload('year');

        $deleteReportsOlderThan = 1;
        $keepBasicMetrics       = false;
        $reportPeriodsToKeep    = array();
        $this->purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics, true);

        $this->assertNumVisits(0, 'day');
        $this->assertNumVisits(0, 'week');
        $this->assertNumVisits(0, 'month');
        $this->assertNumVisits(0, 'year');
        $this->assertHasNoDownload('day');
        $this->assertHasNoDownload('week');
        $this->assertHasNoDownload('month');
        $this->assertHasNoDownload('year');
    }

    private function assertNumVisits($expectedNumVisits, $period)
    {
        $api   = new Request([
            'method' => 'VisitsSummary.getVisits',
            'idSite' => self::$fixture->idSite,
            'date' => self::$fixture->dateTime,
            'period' => $period,
            'format' => 'original',
        ]);
        $table = $api->process();
        $this->assertEquals($expectedNumVisits, $table->getFirstRow()->getColumn('nb_visits'));
    }

    private function assertHasOneDownload($period)
    {
        $api   = new Request($this->getDownloadApiRequestParams($period));
        $table = $api->process();
        $this->assertEquals(1, $table->getRowsCount(), $period . ' should have one download but has not');
    }

    private function assertHasNoDownload($period)
    {
        $api   = new Request($this->getDownloadApiRequestParams($period));
        $table = $api->process();
        $this->assertEquals(0, $table->getRowsCount(), $period . ' should not have a download but has one');
    }

    private function getDownloadApiRequestParams($period)
    {
        return [
            'method' => 'Actions.getDownloads',
            'idSite' => self::$fixture->idSite,
            'date' => self::$fixture->dateTime,
            'period' => $period,
            'format' => 'original',
        ];
    }

    private function purgeData($deleteReportsOlderThan, $reportPeriodsToKeep, $keepBasicMetrics, $keepSegmentReports = false)
    {
        $metricsToKeep           = PrivacyManager::getAllMetricsToKeep();
        $maxRowsToDeletePerQuery = 100000;

        $purger = new ReportsPurger(
            $deleteReportsOlderThan,
            $keepBasicMetrics,
            $reportPeriodsToKeep,
            $keepSegmentReports,
            $metricsToKeep,
            $maxRowsToDeletePerQuery
        );
        $purger->getPurgeEstimate();
        $purger->purgeData();
    }
}

PurgeDataTest::$fixture = new OneVisitorTwoVisits();
