<?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\UserCountryMap;

use Exception;
use Piwik\API\Request;
use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
use Piwik\Piwik;
use Piwik\Site;
use Piwik\Translation\Translator;
use Piwik\View;

require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.php';

class Controller extends \Piwik\Plugin\Controller
{
    // By default plot up to the last 3 days of visitors on the map, for low traffic sites
    public const REAL_TIME_WINDOW = 'last3';

    /**
     * @var Translator
     */
    private $translator;

    public function __construct(Translator $translator)
    {
        $this->translator = $translator;

        parent::__construct();
    }

    public function visitorMap($fetch = false, $segmentOverride = false)
    {
        $this->checkUserCountryPluginEnabled();

        $this->checkSitePermission();
        Piwik::checkUserHasViewAccess($this->idSite);

        $period = Common::getRequestVar('period');
        $date = Common::getRequestVar('date');

        if (!empty($segmentOverride)) {
            $segment = $segmentOverride;
        } else {
            $segment = Request::getRawSegmentFromRequest();
            if (empty($segment)) {
                $segment = '';
            }
        }

        $token_auth = Piwik::getCurrentUserTokenAuth();

        $view = new View('@UserCountryMap/visitorMap');

        // request visits summary
        $request = new Request([
            'method' => 'VisitsSummary.get',
            'format' => 'json',
            'idSite' => $this->idSite,
            'period' => $period,
            'date' => $date,
            'segment' => $segment,
            'token_auth' => $token_auth,
            'filter_limit' => -1,
        ]);
        $config = [];
        $config['visitsSummary'] = json_decode($request->process(), true);
        $config['countryDataUrl'] = $this->report(
            'UserCountry',
            'getCountry',
            $this->idSite,
            $period,
            $date,
            $token_auth,
            false,
            $segment
        );
        $config['regionDataUrl'] = $this->report(
            'UserCountry',
            'getRegion',
            $this->idSite,
            $period,
            $date,
            $token_auth,
            true,
            $segment
        );
        $config['cityDataUrl'] = $this->report(
            'UserCountry',
            'getCity',
            $this->idSite,
            $period,
            $date,
            $token_auth,
            true,
            $segment
        );
        $config['countrySummaryUrl'] = $this->getApiRequestUrl(
            'VisitsSummary',
            'get',
            $this->idSite,
            $period,
            $date,
            $token_auth,
            true,
            $segment
        );
        $view->defaultMetric = array_key_exists('nb_uniq_visitors', $config['visitsSummary'] ?? []) ? 'nb_uniq_visitors' : 'nb_visits';

        $noVisitTranslation = $this->translator->translate('UserCountryMap_NoVisit');
        // some translations containing metric number
        $translations = [
             'nb_visits'            => $this->translator->translate('General_NVisits'),
             'no_visit'             => $noVisitTranslation,
             'nb_actions'           => $this->translator->translate('VisitsSummary_NbActionsDescription'),
             'nb_actions_per_visit' => $this->translator->translate('VisitsSummary_NbActionsPerVisit'),
             'bounce_rate'          => $this->translator->translate('VisitsSummary_NbVisitsBounced'),
             'avg_time_on_site'     => $this->translator->translate('VisitsSummary_AverageVisitDuration'),
             'and_n_others'         => $this->translator->translate('UserCountryMap_AndNOthers'),
             'nb_uniq_visitors'     => $this->translator->translate('General_NUniqueVisitors'),
             'nb_users'             => $this->translator->translate('VisitsSummary_NbUsers'),
        ];

        foreach ($translations as &$translation) {
            if (
                false === strpos($translation, '%s')
                && $translation !== $noVisitTranslation
            ) {
                $translation = '%s ' . $translation;
            }
        }

        $translations['one_visit'] = $this->translator->translate('General_OneVisit');
        $translations['no_data'] = $this->translator->translate('CoreHome_ThereIsNoDataForThisReport');

        $view->localeJSON = json_encode($translations);

        $view->reqParamsJSON = $this->getEnrichedRequest($params = [
            'period'                      => $period,
            'idSite'                      => $this->idSite,
            'date'                        => $date,
            'segment'                     => $segment,
            'token_auth'                  => $token_auth,
            'enable_filter_excludelowpop' => 1,
            'filter_excludelowpop_value'  => -1,
        ]);

        $view->metrics = $config['metrics'] = $this->getMetrics($this->idSite, $period, $date, $token_auth);
        $config['svgBasePath'] = 'plugins/UserCountryMap/svg/';
        $config['mapCssPath'] = 'plugins/UserCountryMap/stylesheets/map.css';
        $view->config = json_encode($config);
        $view->noData = empty($config['visitsSummary']['nb_visits']);

        $countriesByIso = [];
        $regionDataProvider = StaticContainer::get('Piwik\Intl\Data\Provider\RegionDataProvider');
        $countries = array_keys($regionDataProvider->getCountryList());

        foreach ($countries as $country) {
            $countriesByIso[strtoupper($country)] = Piwik::translate('Intl_Country_' . strtoupper($country));
        }

        $view->countriesByIso = $countriesByIso;

        $view->continents = [
            'AF' => \Piwik\Plugins\UserCountry\continentTranslate('afr'),
            'AS' => \Piwik\Plugins\UserCountry\continentTranslate('asi'),
            'EU' => \Piwik\Plugins\UserCountry\continentTranslate('eur'),
            'NA' => \Piwik\Plugins\UserCountry\continentTranslate('amn'),
            'OC' => \Piwik\Plugins\UserCountry\continentTranslate('oce'),
            'SA' => \Piwik\Plugins\UserCountry\continentTranslate('ams'),
        ];

        return $view->render();
    }

    /**
     * Used to build the report Visitor > Real time map
     */
    public function realtimeWorldMap()
    {
        return $this->realtimeMap($standalone = true);
    }

    /**
     * @param bool $standalone When set to true, the Top controls will be hidden to provide better full screen view
     * @param bool $fetch
     * @param bool|string $segmentOverride
     *
     * @return string
     */
    public function realtimeMap($standalone = false, $fetch = false, $segmentOverride = false)
    {
        $this->checkUserCountryPluginEnabled();

        $this->checkSitePermission();
        Piwik::checkUserHasViewAccess($this->idSite);

        $token_auth = Piwik::getCurrentUserTokenAuth();
        $view = new View('@UserCountryMap/realtimeMap');

        $view->mapIsStandaloneNotWidget = !(bool) Common::getRequestVar('widget', $standalone, 'int');

        $view->metrics = $this->getMetrics($this->idSite, 'range', self::REAL_TIME_WINDOW, $token_auth);
        $view->defaultMetric = 'nb_visits';
        $liveRefreshAfterMs = (int)Config::getInstance()->General['live_widget_refresh_after_seconds'] * 1000;

        $goals = Request::processRequest('Goals.getGoals', ['idSite' => $this->idSite, 'filter_limit' => '-1'], $default = []);
        $site = new Site($this->idSite);
        $hasGoals = !empty($goals) || $site->isEcommerceEnabled();

        // maximum number of visits to be displayed in the map
        $maxVisits = Common::getRequestVar('filter_limit', 100, 'int');

        // some translations
        $locale = [
            'nb_actions'       => $this->translator->translate('VisitsSummary_NbActionsDescription'),
            'local_time'       => $this->translator->translate('VisitTime_ColumnLocalTime'),
            'from'             => $this->translator->translate('General_FromReferrer'),
            'seconds'          => $this->translator->translate('Intl_Seconds'),
            'seconds_ago'      => $this->translator->translate('UserCountryMap_SecondsAgo'),
            'minutes'          => $this->translator->translate('Intl_Minutes'),
            'minutes_ago'      => $this->translator->translate('UserCountryMap_MinutesAgo'),
            'hours'            => $this->translator->translate('Intl_Hours'),
            'hours_ago'        => $this->translator->translate('UserCountryMap_HoursAgo'),
            'days_ago'         => $this->translator->translate('UserCountryMap_DaysAgo'),
            'actions'          => $this->translator->translate('Transitions_NumPageviews'),
            'searches'         => $this->translator->translate('UserCountryMap_Searches'),
            'goal_conversions' => $this->translator->translate('UserCountryMap_GoalConversions'),
        ];

        $segment = $segmentOverride ? : Request::getRawSegmentFromRequest() ? : '';
        $params = [
            'period'     => 'range',
            'idSite'     => $this->idSite,
            'segment'    => $segment,
            'token_auth' => $token_auth,
        ];

        $realtimeWindow = Common::getRequestVar('realtimeWindow', self::REAL_TIME_WINDOW, 'string');
        if ($realtimeWindow != 'false') { // handle special value
            $params['date'] = $realtimeWindow;
        }

        $reqParams = $this->getEnrichedRequest($params, $encode = false);

        $view->config = [
            'metrics'            => [],
            'svgBasePath'        => 'plugins/UserCountryMap/svg/',
            'liveRefreshAfterMs' => $liveRefreshAfterMs,
            '_'                  => $locale,
            'reqParams'          => $reqParams,
            'siteHasGoals'       => $hasGoals,
            'maxVisits'          => $maxVisits,
            'changeVisitAlpha'   => Common::getRequestVar('changeVisitAlpha', true, 'int'),
            'removeOldVisits'    => Common::getRequestVar('removeOldVisits', true, 'int'),
            'showFooterMessage'  => Common::getRequestVar('showFooterMessage', true, 'int'),
            'showDateTime'       => Common::getRequestVar('showDateTime', true, 'int'),
            'doNotRefreshVisits' => Common::getRequestVar('doNotRefreshVisits', false, 'int'),
            'enableAnimation'    => Common::getRequestVar('enableAnimation', true, 'int'),
            'forceNowValue'      => Common::getRequestVar('forceNowValue', false, 'int'),
        ];

        return $view->render();
    }

    private function getEnrichedRequest($params, $encode = true)
    {
        $params['format'] = 'json';
        $params['showRawMetrics'] = 1;
        if (empty($params['segment'])) {
            $segment = Request::getRawSegmentFromRequest();
            if (!empty($segment)) {
                $params['segment'] = $segment;
            }
        }

        if (!empty($params['segment'])) {
            $params['segment'] = urldecode($params['segment']);
        }

        if ($encode) {
            $params = json_encode($params);
        }

        return $params;
    }

    private function checkUserCountryPluginEnabled()
    {
        if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('UserCountry')) {
            throw new Exception($this->translator->translate('General_Required', 'Plugin UserCountry'));
        }
    }

    private function getMetrics(
        $idSite,
        $period,
        $date,
        #[\SensitiveParameter]
        $token_auth
    ) {
        $request = new Request([
            'method' => 'API.getMetadata',
            'format' => 'json',
            'apiModule' => 'UserCountry',
            'apiAction' => 'getCountry',
            'idSite' => $idSite,
            'period' => $period,
            'date' => $date,
            'token_auth' => $token_auth,
            'filter_limit' => -1,
        ]);
        $metaData = json_decode($request->process(), true);

        $metrics = [];
        if (!empty($metaData[0]['metrics']) && is_array($metaData[0]['metrics'])) {
            foreach ($metaData[0]['metrics'] as $id => $val) {
                $metrics[] = [$id, $val];
            }
        }
        if (!empty($metaData[0]['processedMetrics']) && is_array($metaData[0]['processedMetrics'])) {
            foreach ($metaData[0]['processedMetrics'] as $id => $val) {
                $metrics[] = [$id, $val];
            }
        }
        return $metrics;
    }

    private function getApiRequestUrl(
        $module,
        $action,
        $idSite,
        $period,
        $date,
        #[\SensitiveParameter]
        $token_auth,
        $filter_by_country = false,
        $segmentOverride = false
    ) {
        // use processed reports
        $url = "?module=" . $module
            . "&method=" . $module . "." . $action . "&format=JSON"
            . "&idSite=" . $idSite
            . "&period=" . $period
            . "&date=" . $date
            . "&token_auth=" . $token_auth
            . "&segment=" . ($segmentOverride ? : Request::getRawSegmentFromRequest())
            . "&enable_filter_excludelowpop=1"
            . "&showRawMetrics=1";

        if ($filter_by_country) {
            $url .= "&filter_column=country"
                . "&filter_sort_column=nb_visits"
                . "&filter_limit=-1"
                . "&filter_pattern=";
        } else {
            $url .= "&filter_limit=-1";
        }
        return $url;
    }

    private function report(
        $module,
        $action,
        $idSite,
        $period,
        $date,
        #[\SensitiveParameter]
        $token_auth,
        $filter_by_country = false,
        $segmentOverride = false
    ) {
        return $this->getApiRequestUrl(
            'API',
            'getProcessedReport&apiModule=' . $module . '&apiAction=' . $action,
            $idSite,
            $period,
            $date,
            $token_auth,
            $filter_by_country,
            $segmentOverride
        );
    }
}
