<?php

namespace Mautic\CoreBundle\Helper\Chart;

use Mautic\CoreBundle\Helper\ColorHelper;

abstract class AbstractChart
{
    use DateRangeUnitTrait;

    /**
     * Datasets of the chart.
     *
     * @var array
     */
    protected $datasets = [];

    /**
     * Labels of the time axe.
     *
     * @var array
     */
    protected $labels = [];

    /**
     * Date from.
     *
     * @var \DateTimeInterface|\DateTime
     */
    protected $dateFrom;

    /**
     * Date to.
     *
     * @var \DateTimeInterface|\DateTime
     */
    protected $dateTo;

    /**
     * Timezone data is requested to be in.
     *
     * @var \DateTimeZone
     */
    protected $timezone;

    /**
     * Time unit.
     *
     * @var string
     */
    protected $unit;

    /**
     * True if unit is H, i, or s.
     *
     * @var bool
     */
    protected $isTimeUnit = false;

    /**
     * amount of items.
     *
     * @var int
     */
    protected $amount;

    /**
     * Default Mautic colors.
     *
     * @var array
     */
    public $colors = ['#4E5D9D', '#00B49C', '#FD9572', '#FDB933', '#757575', '#9C4E5C', '#694535', '#596935'];

    /**
     * Get chart time unit.
     *
     * @return string
     */
    public function getUnit()
    {
        return $this->unit;
    }

    /**
     * Create a DateInterval time unit.
     *
     * @param string $unit
     *
     * @return \DateInterval
     */
    public function getUnitInterval($unit = null)
    {
        if (!$unit) {
            $unit = $this->unit;
        }

        $isTime = in_array($unit, ['H', 'i', 's']) ? 'T' : '';

        if ('i' == $unit) {
            $unit = 'M';
        }

        return new \DateInterval('P'.$isTime.'1'.strtoupper($unit));
    }

    /**
     * Helper function to shorten/truncate a string.
     *
     * @param string $string
     * @param int    $length
     * @param string $append
     *
     * @return string
     */
    public static function truncate($string, $length = 100, $append = '...')
    {
        $string = trim($string);

        if (strlen($string) > $length) {
            $string = wordwrap($string, $length);
            $string = explode("\n", $string, 2);
            $string = $string[0].$append;
        }

        return $string;
    }

    /**
     * Sets the clones of the date range and validates it.
     */
    public function setDateRange(\DateTimeInterface $dateFrom, \DateTimeInterface $dateTo): void
    {
        $this->timezone = $dateFrom->getTimezone();
        /** @var \DateTime $dateFrom */
        $this->dateFrom = clone $dateFrom;
        /** @var \DateTime $dateTo */
        $this->dateTo   = clone $dateTo;

        // a diff of two identical dates returns 0, but we expect 24 hours
        if ($dateFrom == $dateTo) {
            $this->dateTo->modify('+1 day');
        }

        // If today, adjust dateTo to be end of today if unit is not time based or to the current hour if it is
        if (!$this->isTimeUnit) {
            $this->dateTo->setTime(23, 59, 59);

            return;
        }

        // If time aware and the to date is today, set the stats to the current hour to avoid empty future hours in graphs
        $now = new \DateTime();
        if ($now->format('Y-m-d') === $this->dateTo->format('Y-m-d')) {
            $this->dateTo = $now;
        }
    }

    /**
     * Modify the date to add one current time unit to it and subtract 1 second.
     * Can be used to get the current day results.
     */
    public function addOneUnitMinusOneSec(\DateTime|\DateTimeImmutable &$date): \DateTime|\DateTimeImmutable
    {
        return $date->add($this->getUnitInterval())->modify('-1 sec');
    }

    /**
     * Count amount of time slots of a time unit from a date range.
     *
     * @return int
     */
    public function countAmountFromDateRange()
    {
        switch ($this->unit) {
            case 's':
                $value  = (int) $this->dateTo->diff($this->dateFrom)->format('%s');
                $amount = $value + 1;
                break;
            case 'i':
                $value  = (int) $this->dateTo->diff($this->dateFrom)->format('%i');
                $amount = $value + 1;
                break;
            case 'd':
                $value  = (int) $this->dateTo->diff($this->dateFrom)->format('%a');
                $amount = $value + 1;
                break;
            case 'W':
                $dayAmount = (int) $this->dateTo->diff($this->dateFrom)->format('%a');
                $amount    = (int) (ceil($dayAmount / 7) + 1);
                break;
            case 'm':
                $dateDiff = $this->dateTo->diff($this->dateFrom);
                $years    = (int) $dateDiff->format('%y');
                $months   = (int) $dateDiff->format('%m');
                $amount   = $years * 12 + $months;

                // Add 1 month if there are some days left
                if ((int) $dateDiff->format('%d') > 0) {
                    ++$amount;
                }

                // Add 1 month if count of days are greater or equal than in date to
                if ((int) $this->dateFrom->format('d') >= (int) $this->dateTo->format('d')) {
                    ++$amount;
                }
                break;
            case 'H':
                $dateDiff = $this->dateTo->diff($this->dateFrom);
                $amount   = $dateDiff->h + $dateDiff->days * 24;
                ++$amount;
                break;
            default:
                $value  = (int) $this->dateTo->diff($this->dateFrom)->format('%'.$this->unit);
                $amount = $value + 1;
                break;
        }

        return $amount;
    }

    /**
     * Generate unique color for the dataset.
     *
     * @param int $datasetId
     *
     * @return ColorHelper
     */
    public function configureColorHelper($datasetId)
    {
        $colorHelper = new ColorHelper();

        if (isset($this->colors[$datasetId])) {
            $color = $colorHelper->setHex($this->colors[$datasetId]);
        } else {
            $color = $colorHelper->buildRandomColor();
        }

        return $color;
    }
}
