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

use Piwik\API\Request;
use Piwik\Common;
use Piwik\Config;
use Piwik\Date;
use Piwik\DataTable;
use Piwik\Metrics\Formatter;
use Matomo\Network\IPUtils;
use Piwik\Piwik;
use Piwik\Site;
use Piwik\Plugins\SitesManager\API as APISitesManager;
use Piwik\Plugins\Referrers\API as APIReferrers;
use Piwik\View;

class VisitorDetails extends VisitorDetailsAbstract
{
    /**
     * @var array<int, array<string>>
     */
    private $cachedAdditionalSiteUrls = [];

    public function extendVisitorDetails(&$visitor)
    {
        $idSite     = $this->getIdSite();
        $website    = new Site($idSite);
        $timezone   = $website->getTimezone();
        $currency   = $website->getCurrency();
        $currencies = APISitesManager::getInstance()->getCurrencySymbols();

        $visitor += [
            'idSite'              => $idSite,
            'idVisit'             => $this->getIdVisit(),
            'visitIp'             => $this->getIp(),
            'visitorId'           => $this->getVisitorId(),
            'fingerprint'         => $this->getFingerprint(),

            // => false are placeholders to be filled in API later
            'actionDetails'       => false,
            'goalConversions'     => false,
            'siteCurrency'        => false,
            'siteCurrencySymbol'  => false,

            // all time entries
            'serverDate'          => $this->getServerDate(),
            'visitServerHour'     => $this->getVisitServerHour(),
            'lastActionTimestamp' => $this->getTimestampLastAction(),
            'lastActionDateTime'  => $this->getDateTimeLastAction(),
        ];

        $visitor['siteCurrency']         = $currency;
        $visitor['siteCurrencySymbol']   = @$currencies[$visitor['siteCurrency']];
        $visitor['siteName']             = $website->getName();
        $visitor['serverTimestamp']      = $visitor['lastActionTimestamp'];
        $visitor['firstActionTimestamp'] = strtotime($this->details['visit_first_action_time']);

        $dateTimeVisit = Date::factory($visitor['lastActionTimestamp'], $timezone);
        if ($dateTimeVisit) {
            $visitor['serverTimePretty'] = $dateTimeVisit->getLocalized(Date::TIME_FORMAT);
            $visitor['serverDatePretty'] = $dateTimeVisit->getLocalized(Date::DATE_FORMAT_LONG);
        }

        $dateTimeVisitFirstAction               = Date::factory($visitor['firstActionTimestamp'], $timezone);
        $visitor['serverDatePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Date::DATE_FORMAT_LONG);
        $visitor['serverTimePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Date::TIME_FORMAT);
    }

    public function renderAction($action, $previousAction, $visitorDetails)
    {
        if (empty($action['type'])) {
            return;
        }

        switch ($action['type']) {
            case 'ecommerceOrder':
            case 'ecommerceAbandonedCart':
                $template = '@Live/_actionEcommerce.twig';
                break;
            case 'goal':
                if (empty($action['goalName'])) {
                    return; // goal deleted
                }
                $template = '@Live/_actionGoal.twig';
                break;
            case 'action':
            case 'search':
            case 'outlink':
            case 'download':
                $template = '@Live/_actionCommon.twig';
                break;
        }

        if (empty($template)) {
            return;
        }

        if (isset($action['type']) && in_array($action['type'], ['outlink', 'download']) && isset($action['url'])) {
            $action['url'] = html_entity_decode($action['url'], ENT_QUOTES, "UTF-8");
        }

        $idSite = $this->getIdSite();

        $view                 = new View($template);
        $view->sendHeadersWhenRendering = false;
        $view->mainUrl        = trim(Site::getMainUrlFor($idSite));
        $view->additionalUrls = $this->getAdditionalUrlsForSite($idSite);
        $view->action         = $action;
        $view->previousAction = $previousAction;
        $view->visitInfo      = $visitorDetails;
        return $view->render();
    }

    public function renderActionTooltip($action, $visitInfo)
    {
        $view            = new View('@Live/_actionTooltip');
        $view->sendHeadersWhenRendering = false;
        $view->action    = $action;
        $view->visitInfo = $visitInfo;
        return [[ 0, $view->render() ]];
    }

    public function renderVisitorDetails($visitorDetails)
    {
        $view            = new View('@Live/_visitorDetails.twig');
        $view->isProfileEnabled = Live::isVisitorProfileEnabled();
        $view->sendHeadersWhenRendering = false;
        $view->visitInfo = $visitorDetails;
        return [[ 0, $view->render() ]];
    }

    public function renderIcons($visitorDetails)
    {
        $view          = new View('@Live/_visitorLogIcons.twig');
        $view->isProfileEnabled = Live::isVisitorProfileEnabled();
        $view->sendHeadersWhenRendering = false;
        $view->visitor = $visitorDetails;
        return $view->render();
    }

    public function getVisitorId()
    {
        if (isset($this->details['idvisitor'])) {
            return bin2hex($this->details['idvisitor']);
        }
        return false;
    }

    public function getVisitServerHour()
    {
        return date('G', strtotime($this->details['visit_last_action_time']));
    }

    public function getServerDate()
    {
        return date('Y-m-d', strtotime($this->details['visit_last_action_time']));
    }

    public function getIp()
    {
        if (isset($this->details['location_ip'])) {
            return IPUtils::binaryToStringIP($this->details['location_ip']);
        }
        return null;
    }

    public function getIdVisit()
    {
        return $this->details['idvisit'];
    }

    public function getIdSite()
    {
        return isset($this->details['idsite']) ? $this->details['idsite'] : Common::getRequestVar('idSite');
    }

    public function getFingerprint()
    {
        if (isset($this->details['config_id'])) {
            return bin2hex($this->details['config_id']);
        }
        return false;
    }

    public function getTimestampLastAction()
    {
        return strtotime($this->details['visit_last_action_time']);
    }

    public function getDateTimeLastAction()
    {
        return date('Y-m-d H:i:s', strtotime($this->details['visit_last_action_time']));
    }


    public function initProfile($visits, &$profile)
    {
        $profile['totalVisits']        = 0;
        $profile['totalVisitDuration'] = 0;
    }

    public function handleProfileVisit($visit, &$profile)
    {
        ++$profile['totalVisits'];

        $profile['totalVisitDuration'] += $visit->getColumn('visitDuration');
    }


    public function finalizeProfile($visits, &$profile)
    {
        $site = new Site($this->getIdSite());
        $formatter = new Formatter();

        $profile['totalVisitDurationPretty'] = $formatter->getPrettyTimeFromSeconds($profile['totalVisitDuration'], true);

        $rows = $visits->getRows();
        $firstVisit = $profile['visit_first'];

        if (count($rows) >= Config::getInstance()->General['live_visitor_profile_max_visits_to_aggregate']) {
            $firstVisit = $this->fetchFirstVisit();
        }

        $profile['userId']           = $visits->getLastRow()->getColumn('userId');
        $profile['firstVisit']       = $this->getVisitorProfileVisitSummary($firstVisit, $site);
        $profile['lastVisit']        = $this->getVisitorProfileVisitSummary($profile['visit_last'], $site);
        $profile['visitsAggregated'] = count($rows);
    }

    /**
     * Fetch first visit from Live API
     *
     * @return DataTable\Row
     */
    private function fetchFirstVisit()
    {
        $response = Request::processRequest('Live.getFirstVisitForVisitorId', [
            'idSite' => $this->getIdSite(),
            'visitorId' => $this->getVisitorId(),
        ]);

        return $response->getFirstRow();
    }

    /**
     * Returns a summary for an important visit. Used to describe the first & last visits of a visitor.
     *
     * @param DataTable\Row $visit
     * @return array
     */
    private function getVisitorProfileVisitSummary($visit, Site $site): array
    {
        $today = Date::factory('today', $site->getTimezone());
        $firstActionTimestamp = $visit->getColumn('firstActionTimestamp');
        $firstActionDate = Date::factory($firstActionTimestamp, $site->getTimezone());

        return [
            'date'            => $firstActionTimestamp,
            'prettyDate'      => $firstActionDate->getLocalized(Date::DATE_FORMAT_LONG),
            'daysAgo'         => (int) Date::secondsToDays($today->getTimestamp() - $firstActionDate->getTimestamp()),
            'referrerType'    => $visit->getColumn('referrerType'),
            'referrerUrl'     => $visit->getColumn('referrerUrl') ?: '',
            'referralSummary' => self::getReferrerSummaryForVisit($visit),
        ];
    }

    /**
     * Returns a summary for a visit's referral.
     *
     * @param DataTable\Row $visit
     * @return bool|mixed|string
     */
    public static function getReferrerSummaryForVisit($visit)
    {
        $referrerType = $visit->getColumn('referrerType');
        if (
            $referrerType === false
            || $referrerType == 'direct'
        ) {
            return Piwik::translate('Referrers_DirectEntry');
        }

        if ($referrerType == 'search') {
            $referrerName = $visit->getColumn('referrerName');

            $keyword = $visit->getColumn('referrerKeyword');
            if (
                $keyword !== false
                && $keyword != APIReferrers::getKeywordNotDefinedString()
            ) {
                $referrerName .= ' (' . $keyword . ')';
            }
            return $referrerName;
        }

        if ($referrerType == 'campaign') {
            $summary = Piwik::translate('Referrers_ColumnCampaign') . ': ' . $visit->getColumn('referrerName');
            $keyword = $visit->getColumn('referrerKeyword');
            if (!empty($keyword)) {
                $summary .= ' - ' . $keyword;
            }

            return $summary;
        }

        return $visit->getColumn('referrerName');
    }

    /**
     * @return array<int, array<string>>
     */
    private function getAdditionalUrlsForSite(int $idSite): array
    {
        if (isset($this->cachedAdditionalSiteUrls[$idSite])) {
            return $this->cachedAdditionalSiteUrls[$idSite];
        }

        $sitesModel = new \Piwik\Plugins\SitesManager\Model();
        $additionalSiteUrls = $sitesModel->getAliasSiteUrlsFromId($idSite);

        $this->cachedAdditionalSiteUrls[$idSite] = $additionalSiteUrls;

        return $additionalSiteUrls;
    }
}
