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

use Piwik\Piwik;
use Piwik\Plugin\Segment;
/**
 * A factory to create segments from a dimension.
 *
 * @api since Matomo 4.0.0
 */
class DimensionSegmentFactory
{
    /**
     * @var Dimension
     */
    private $dimension = null;
    /**
     * Generates a new dimension segment factory.
     * @param Dimension $dimension A dimension instance the created segments should be based on.
     */
    public function __construct(\Piwik\Columns\Dimension $dimension)
    {
        $this->dimension = $dimension;
    }
    /**
     * Creates a segment based on the dimension properties
     *
     * @param Segment|null $segment optional Segment to enrich with dimension data (if properties not already set)
     * @return Segment
     * @throws \Exception
     */
    public function createSegment(?Segment $segment = null)
    {
        $dimension = $this->dimension;
        if (!$segment instanceof Segment) {
            $segment = new Segment();
        }
        $segment->dimension = $dimension;
        if (!$segment->getSegment() && $dimension->getSegmentName()) {
            $segment->setSegment($dimension->getSegmentName());
        }
        if (!$segment->getType()) {
            $metricTypes = array(\Piwik\Columns\Dimension::TYPE_NUMBER, \Piwik\Columns\Dimension::TYPE_FLOAT, \Piwik\Columns\Dimension::TYPE_MONEY, \Piwik\Columns\Dimension::TYPE_DURATION_S, \Piwik\Columns\Dimension::TYPE_DURATION_MS);
            if (in_array($dimension->getType(), $metricTypes, $strict = \true)) {
                $segment->setType(Segment::TYPE_METRIC);
            } else {
                $segment->setType(Segment::TYPE_DIMENSION);
            }
        }
        if (!$segment->getCategoryId() && $dimension->getCategoryId()) {
            $segment->setCategory($dimension->getCategoryId());
        }
        if (!$segment->getName() && $dimension->getName()) {
            $segment->setName($dimension->getName());
        }
        $sqlSegment = $segment->getSqlSegment();
        if (empty($sqlSegment) && !$segment->getUnionOfSegments()) {
            if (!empty($dimension->getSqlSegment())) {
                $segment->setSqlSegment($dimension->getSqlSegment());
            } elseif ($dimension->getDbTableName() && $dimension->getColumnName()) {
                $segment->setSqlSegment($dimension->getDbTableName() . '.' . $dimension->getColumnName());
            } else {
                throw new \Exception('Segment cannot be added because no sql segment is set');
            }
        }
        $acceptValues = $dimension->getAcceptValues() ?: $this->guessAcceptValues();
        if ($acceptValues && !$segment->getAcceptValues()) {
            $segment->setAcceptedValues($acceptValues);
        }
        $suggestedValuesCallback = $dimension->getSuggestedValuesCallback() ?: $this->guessSuggestedValuesCallback();
        if ($suggestedValuesCallback && !$segment->getSuggestedValuesCallback()) {
            $segment->setSuggestedValuesCallback($suggestedValuesCallback);
        }
        if ($dimension->getSuggestedValuesApi()) {
            $segment->setSuggestedValuesApi($dimension->getSuggestedValuesApi());
        }
        $sqlFilter = $dimension->getSqlFilter();
        if (!$sqlFilter && !$dimension->getSqlFilterValue() && !$segment->getSqlFilter() && !$segment->getSqlFilterValue()) {
            $sqlFilter = $this->guessSqlFilter();
        }
        if ($dimension->getSqlFilterValue() && !$segment->getSqlFilterValue()) {
            $segment->setSqlFilterValue($dimension->getSqlFilterValue());
        }
        if ($sqlFilter && !$segment->getSqlFilter()) {
            $segment->setSqlFilter($sqlFilter);
        }
        if (!$dimension->isAnonymousAllowed()) {
            $segment->setRequiresRegisteredUser(\true);
        }
        return $segment;
    }
    protected function guessSqlFilter()
    {
        $sqlFilterValue = null;
        $enum = $this->dimension->getEnumColumnValues();
        if (!empty($enum)) {
            $sqlFilterValue = function ($value, $sqlSegmentName) use($enum) {
                if (isset($enum[$value])) {
                    return $value;
                }
                $id = array_search($value, $enum);
                if ($id === \false) {
                    $id = array_search(strtolower(trim(urldecode($value))), $enum);
                    if ($id === \false) {
                        throw new \Exception("Invalid '{$sqlSegmentName}' segment value {$value}");
                    }
                }
                return $id;
            };
        }
        return $sqlFilterValue;
    }
    protected function guessAcceptValues()
    {
        $acceptValues = null;
        // we can generate accept values for enums automatically
        $enum = $this->dimension->getEnumColumnValues();
        if (!empty($enum)) {
            $enumValues = array_values($enum);
            $enumValues = array_slice($enumValues, 0, 20);
            $acceptValues = Piwik::translate('General_ForExampleShort') . ' ' . implode(', ', $enumValues);
        }
        return $acceptValues;
    }
    protected function guessSuggestedValuesCallback()
    {
        $suggestedValuesCallback = null;
        // we can generate efficient value callback for enums automatically
        $enum = $this->dimension->getEnumColumnValues();
        if (!empty($enum)) {
            $suggestedValuesCallback = function ($idSite, $maxValuesToReturn) use($enum) {
                $values = array_values($enum);
                return array_slice($values, 0, $maxValuesToReturn);
            };
        }
        return $suggestedValuesCallback;
    }
}
