<?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\Events\RecordBuilders;

use Piwik\ArchiveProcessor;
use Piwik\ArchiveProcessor\Record;
use Piwik\ArchiveProcessor\RecordBuilder;
use Piwik\Config\GeneralConfig;
use Piwik\DataAccess\LogAggregator;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Plugins\Actions\ArchivingHelper;
use Piwik\Plugins\Events\Archiver;
use Piwik\RankingQuery;
use Piwik\Tracker\Action;
class EventReports extends RecordBuilder
{
    public function __construct()
    {
        parent::__construct();
        $this->columnToSortByBeforeTruncation = Metrics::INDEX_NB_VISITS;
        $this->columnAggregationOps = [Metrics::INDEX_EVENT_MIN_EVENT_VALUE => 'min', Metrics::INDEX_EVENT_MAX_EVENT_VALUE => 'max'];
    }
    public function getRecordMetadata(ArchiveProcessor $archiveProcessor) : array
    {
        $idSite = $archiveProcessor->getParams()->getSite()->getId();
        $maximumRowsInDataTable = GeneralConfig::getConfigValue('datatable_archiving_maximum_rows_events', $idSite);
        $maximumRowsInSubDataTable = GeneralConfig::getConfigValue('datatable_archiving_maximum_rows_subtable_events', $idSite);
        /** @var Record[] $records */
        $records = [Record::make(Record::TYPE_BLOB, Archiver::EVENTS_CATEGORY_ACTION_RECORD_NAME), Record::make(Record::TYPE_BLOB, Archiver::EVENTS_CATEGORY_NAME_RECORD_NAME), Record::make(Record::TYPE_BLOB, Archiver::EVENTS_ACTION_CATEGORY_RECORD_NAME), Record::make(Record::TYPE_BLOB, Archiver::EVENTS_ACTION_NAME_RECORD_NAME), Record::make(Record::TYPE_BLOB, Archiver::EVENTS_NAME_ACTION_RECORD_NAME), Record::make(Record::TYPE_BLOB, Archiver::EVENTS_NAME_CATEGORY_RECORD_NAME)];
        foreach ($records as $record) {
            $record->setMaxRowsInTable($maximumRowsInDataTable)->setMaxRowsInSubtable($maximumRowsInSubDataTable)->setBlobColumnAggregationOps($this->columnAggregationOps);
        }
        return $records;
    }
    protected function getRecordToDimensions()
    {
        return [Archiver::EVENTS_CATEGORY_ACTION_RECORD_NAME => ["eventCategory", "eventAction"], Archiver::EVENTS_CATEGORY_NAME_RECORD_NAME => ["eventCategory", "eventName"], Archiver::EVENTS_ACTION_NAME_RECORD_NAME => ["eventAction", "eventName"], Archiver::EVENTS_ACTION_CATEGORY_RECORD_NAME => ["eventAction", "eventCategory"], Archiver::EVENTS_NAME_ACTION_RECORD_NAME => ["eventName", "eventAction"], Archiver::EVENTS_NAME_CATEGORY_RECORD_NAME => ["eventName", "eventCategory"]];
    }
    protected function aggregate(ArchiveProcessor $archiveProcessor) : array
    {
        $logAggregator = $archiveProcessor->getLogAggregator();
        $reports = [];
        $select = "\n                log_action_event_category.name as eventCategory,\n                log_action_event_action.name as eventAction,\n                log_action_event_name.name as eventName,\n\n\t\t\t\tcount(distinct log_link_visit_action.idvisit) as `" . Metrics::INDEX_NB_VISITS . "`,\n\t\t\t\tcount(distinct log_link_visit_action.idvisitor) as `" . Metrics::INDEX_NB_UNIQ_VISITORS . "`,\n\t\t\t\tcount(*) as `" . Metrics::INDEX_EVENT_NB_HITS . "`,\n\n\t\t\t\tsum(\n\t\t\t\t\tcase when " . Action::DB_COLUMN_CUSTOM_FLOAT . " is null\n\t\t\t\t\t\tthen 0\n\t\t\t\t\t\telse " . Action::DB_COLUMN_CUSTOM_FLOAT . "\n\t\t\t\t\tend\n\t\t\t\t) as `" . Metrics::INDEX_EVENT_SUM_EVENT_VALUE . "`,\n\t\t\t\tsum( case when " . Action::DB_COLUMN_CUSTOM_FLOAT . " is null then 0 else 1 end )\n\t\t\t\t    as `" . Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE . "`,\n\t\t\t\tmin(" . Action::DB_COLUMN_CUSTOM_FLOAT . ") as `" . Metrics::INDEX_EVENT_MIN_EVENT_VALUE . "`,\n\t\t\t\tmax(" . Action::DB_COLUMN_CUSTOM_FLOAT . ") as `" . Metrics::INDEX_EVENT_MAX_EVENT_VALUE . "`\n        ";
        $from = ["log_link_visit_action", ["table" => "log_action", "tableAlias" => "log_action_event_category", "joinOn" => "log_link_visit_action.idaction_event_category = log_action_event_category.idaction"], ["table" => "log_action", "tableAlias" => "log_action_event_action", "joinOn" => "log_link_visit_action.idaction_event_action = log_action_event_action.idaction"], ["table" => "log_action", "tableAlias" => "log_action_event_name", "joinOn" => "log_link_visit_action.idaction_name = log_action_event_name.idaction"]];
        $where = $logAggregator->getWhereStatement('log_link_visit_action', 'server_time');
        $where .= " AND log_link_visit_action.idaction_event_category IS NOT NULL";
        $groupBy = "log_link_visit_action.idaction_event_category,\n                    log_link_visit_action.idaction_event_action,\n                    log_link_visit_action.idaction_name";
        $orderBy = "`" . Metrics::INDEX_NB_VISITS . "` DESC, `eventName`";
        $rankingQueryLimit = ArchivingHelper::getRankingQueryLimit();
        $rankingQuery = null;
        if ($rankingQueryLimit > 0) {
            $rankingQuery = new RankingQuery($rankingQueryLimit);
            $rankingQuery->addLabelColumn(['eventCategory', 'eventAction', 'eventName']);
            $rankingQuery->addColumn([Metrics::INDEX_NB_UNIQ_VISITORS]);
            $rankingQuery->addColumn([Metrics::INDEX_EVENT_NB_HITS, Metrics::INDEX_NB_VISITS, Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE], 'sum');
            $rankingQuery->addColumn(Metrics::INDEX_EVENT_SUM_EVENT_VALUE, 'sum');
            $rankingQuery->addColumn(Metrics::INDEX_EVENT_MIN_EVENT_VALUE, 'min');
            $rankingQuery->addColumn(Metrics::INDEX_EVENT_MAX_EVENT_VALUE, 'max');
        }
        $this->archiveDayQueryProcess($reports, $logAggregator, $select, $from, $where, $groupBy, $orderBy, $rankingQuery);
        return $reports;
    }
    protected function archiveDayQueryProcess(array &$reports, LogAggregator $logAggregator, string $select, array $from, string $where, string $groupBy, string $orderBy, ?RankingQuery $rankingQuery = null) : void
    {
        // get query with segmentation
        $query = $logAggregator->generateQuery($select, $from, $where, $groupBy, $orderBy);
        // apply ranking query
        if ($rankingQuery) {
            $query['sql'] = $rankingQuery->generateRankingQuery($query['sql']);
        }
        // get result
        $resultSet = $logAggregator->getDb()->query($query['sql'], $query['bind']);
        if ($resultSet === \false) {
            return;
        }
        while ($row = $resultSet->fetch()) {
            $this->aggregateEventRow($reports, $row);
        }
    }
    protected function aggregateEventRow(array &$reports, array $row) : void
    {
        foreach ($this->getRecordToDimensions() as $record => $dimensions) {
            if (empty($reports[$record])) {
                $reports[$record] = new DataTable();
            }
            /** @var DataTable $table */
            $table = $reports[$record];
            $mainDimension = $dimensions[0];
            $mainLabel = $row[$mainDimension];
            // Event name is optional
            if ($mainDimension == 'eventName' && empty($mainLabel)) {
                $mainLabel = Archiver::EVENT_NAME_NOT_SET;
            }
            $columns = [Metrics::INDEX_NB_UNIQ_VISITORS => $row[Metrics::INDEX_NB_UNIQ_VISITORS] ?? 0, Metrics::INDEX_NB_VISITS => $row[Metrics::INDEX_NB_VISITS] ?? 0, Metrics::INDEX_EVENT_NB_HITS => $row[Metrics::INDEX_EVENT_NB_HITS] ?? 0, Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE => $row[Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE] ?? 0, Metrics::INDEX_EVENT_SUM_EVENT_VALUE => round($row[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] ?? 0, 2), Metrics::INDEX_EVENT_MIN_EVENT_VALUE => is_numeric($row[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]) ? round($row[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], 2) : null, Metrics::INDEX_EVENT_MAX_EVENT_VALUE => is_numeric($row[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]) ? round($row[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], 2) : null];
            $topLevelRow = $table->sumRowWithLabel($mainLabel, $columns, $this->columnAggregationOps);
            $subDimension = $dimensions[1];
            $subLabel = $row[$subDimension];
            if (empty($subLabel)) {
                continue;
            }
            $topLevelRow->sumRowWithLabelToSubtable($subLabel, $columns, $this->columnAggregationOps);
        }
    }
}
