<?php
declare(strict_types=1);

/**
 * Controller to handle application statistics.
 */
class FreshRSS_stats_Controller extends FreshRSS_ActionController {

	/**
	 * @var FreshRSS_ViewStats
	 * @phpstan-ignore property.phpDocType
	 */
	protected $view;

	public function __construct() {
		parent::__construct(FreshRSS_ViewStats::class);
	}

	/**
	 * This action is called before every other action in that class. It is
	 * the common boilerplate for every action. It is triggered by the
	 * underlying framework.
	 */
	#[\Override]
	public function firstAction(): void {
		if (!FreshRSS_Auth::hasAccess()) {
			Minz_Error::error(403);
		}

		$this->_csp([
			'default-src' => "'self'",
			'frame-ancestors' => FreshRSS_Context::systemConf()->attributeString('csp.frame-ancestors') ?? "'none'",
			'img-src' => '* data: blob:',
		]);

		$catDAO = FreshRSS_Factory::createCategoryDao();
		$catDAO->checkDefault();
		$this->view->categories = $catDAO->listSortedCategories(prePopulateFeeds: false);

		FreshRSS_View::prependTitle(_t('admin.stats.title') . ' · ');
	}

	/**
	 * This action handles the statistic main page.
	 *
	 * It displays the statistic main page.
	 * The values computed to display the page are:
	 *   - repartition of read/unread/favorite/not favorite (repartition)
	 *   - number of article per day (entryCount)
	 *   - number of feed by category (feedByCategory)
	 *   - number of article by category (entryByCategory)
	 *   - list of most prolific feed (topFeed)
	 */
	public function indexAction(): void {
		$statsDAO = FreshRSS_Factory::createStatsDAO();
		FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.umd.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.umd.min.js')));

		$this->view->repartitions = $statsDAO->calculateEntryRepartition();

		$entryCount = $statsDAO->calculateEntryCount();
		if (count($entryCount) > 0) {
			$this->view->entryCount = $entryCount;
			$this->view->average = round(array_sum(array_values($entryCount)) / count($entryCount), 2);
		} else {
			$this->view->entryCount = [];
			$this->view->average = -1.0;
		}

		$feedByCategory = [];
		$feedByCategory_calculated = $statsDAO->calculateFeedByCategory();
		for ($i = 0; $i < count($feedByCategory_calculated); $i++) {
			$feedByCategory['label'][$i] = $feedByCategory_calculated[$i]['label'];
			$feedByCategory['data'][$i] = $feedByCategory_calculated[$i]['data'];
		}
		$this->view->feedByCategory = $feedByCategory;

		$entryByCategory = [];
		$entryByCategory_calculated = $statsDAO->calculateEntryByCategory();
		for ($i = 0; $i < count($entryByCategory_calculated); $i++) {
			$entryByCategory['label'][$i] = $entryByCategory_calculated[$i]['label'];
			$entryByCategory['data'][$i] = $entryByCategory_calculated[$i]['data'];
		}
		$this->view->entryByCategory = $entryByCategory;

		$this->view->topFeed = $statsDAO->calculateTopFeed();

		$last30DaysLabels = [];
		for ($i = 0; $i < 31; $i++) {
			$last30DaysLabels[$i] = date('d.m.Y', strtotime((-30 + $i) . ' days') ?: null);
		}

		$this->view->last30DaysLabels = $last30DaysLabels;
	}

	/**
	 * This action handles the feed action on the idle statistic page.
	 * set the 'from' parameter to remember that it had a redirection coming from stats controller,
	 * to use the subscription controller to save it,
	 * but shows the stats idle page
	 */
	public function feedAction(): void {
		$id = Minz_Request::paramInt('id');
		$ajax = Minz_Request::paramBoolean('ajax');
		if ($ajax) {
			$url_redirect = ['c' => 'subscription', 'a' => 'feed', 'params' => ['id' => (string)$id, 'from' => 'stats', 'ajax' => (string)$ajax]];
		} else {
			$url_redirect = ['c' => 'subscription', 'a' => 'feed', 'params' => ['id' => (string)$id, 'from' => 'stats']];
		}
		Minz_Request::forward($url_redirect, true);
	}

	/**
	 * This action handles the idle feed statistic page.
	 *
	 * It displays the list of idle feed for different period. The supported
	 * periods are:
	 *   - last 5 years
	 *   - last 3 years
	 *   - last 2 years
	 *   - last year
	 *   - last 6 months
	 *   - last 3 months
	 *   - last month
	 *   - last week
	 */
	public function idleAction(): void {
		FreshRSS_View::appendScript(Minz_Url::display('/scripts/feed.js?' . @filemtime(PUBLIC_PATH . '/scripts/feed.js')));
		$feed_dao = FreshRSS_Factory::createFeedDao();
		$statsDAO = FreshRSS_Factory::createStatsDAO();
		$feeds = $statsDAO->calculateFeedLastDate();
		$idleFeeds = [
			'last_5_year' => [],
			'last_3_year' => [],
			'last_2_year' => [],
			'last_year' => [],
			'last_6_month' => [],
			'last_3_month' => [],
			'last_month' => [],
			'last_week' => [],
		];
		$now = new \DateTime();
		$feedDate = clone $now;
		$lastWeek = clone $now;
		$lastWeek->modify('-1 week');
		$lastMonth = clone $now;
		$lastMonth->modify('-1 month');
		$last3Month = clone $now;
		$last3Month->modify('-3 month');
		$last6Month = clone $now;
		$last6Month->modify('-6 month');
		$lastYear = clone $now;
		$lastYear->modify('-1 year');
		$last2Year = clone $now;
		$last2Year->modify('-2 year');
		$last3Year = clone $now;
		$last3Year->modify('-3 year');
		$last5Year = clone $now;
		$last5Year->modify('-5 year');

		foreach ($feeds as $feed) {
			$feedDAO = FreshRSS_Factory::createFeedDao();
			$feedObject = $feedDAO->searchById($feed['id']);
			if ($feedObject !== null) {
				$feed['favicon'] = $feedObject->favicon();
			}

			$feedDate->setTimestamp($feed['last_date']);
			if ($feedDate >= $lastWeek) {
				continue;
			}
			if ($feedDate < $last5Year) {
				$idleFeeds['last_5_year'][] = $feed;
			} elseif ($feedDate < $last3Year) {
				$idleFeeds['last_3_year'][] = $feed;
			} elseif ($feedDate < $last2Year) {
				$idleFeeds['last_2_year'][] = $feed;
			} elseif ($feedDate < $lastYear) {
				$idleFeeds['last_year'][] = $feed;
			} elseif ($feedDate < $last6Month) {
				$idleFeeds['last_6_month'][] = $feed;
			} elseif ($feedDate < $last3Month) {
				$idleFeeds['last_3_month'][] = $feed;
			} elseif ($feedDate < $lastMonth) {
				$idleFeeds['last_month'][] = $feed;
			} elseif ($feedDate < $lastWeek) {
				$idleFeeds['last_week'][] = $feed;
			}
		}

		$this->view->idleFeeds = $idleFeeds;
		$this->view->feeds = $feed_dao->listFeeds();

		$id = Minz_Request::paramInt('id');
		$this->view->displaySlider = false;
		if ($id !== 0) {
			$this->view->displaySlider = true;
			$feedDAO = FreshRSS_Factory::createFeedDao();
			$this->view->feed = $feedDAO->searchById($id) ?? FreshRSS_Feed::default();
		}
	}

	/**
	 * This action handles the article repartition statistic page.
	 *
	 * It displays the number of article and the average of article for the
	 * following periods:
	 *   - hour of the day
	 *   - day of the week
	 *   - month
	 *
	 * @todo verify that the metrics used here make some sense. Especially
	 *       for the average.
	 */
	public function repartitionAction(): void {
		$statsDAO 		= FreshRSS_Factory::createStatsDAO();
		$categoryDAO 	= FreshRSS_Factory::createCategoryDao();
		$feedDAO 		= FreshRSS_Factory::createFeedDao();

		FreshRSS_View::appendScript(Minz_Url::display('/scripts/vendor/chart.umd.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/vendor/chart.umd.min.js')));

		$id = Minz_Request::paramInt('id');
		if ($id === 0) {
			$id = null;
		}

		$this->view->categories 	= $categoryDAO->listCategories(prePopulateFeeds: true);
		$this->view->feed 			= $id === null ? FreshRSS_Feed::default() : ($feedDAO->searchById($id) ?? FreshRSS_Feed::default());
		$this->view->days 			= $statsDAO->getDays();
		$this->view->months 		= $statsDAO->getMonths();

		$this->view->repartition 			= $statsDAO->calculateEntryRepartitionPerFeed($id);

		$this->view->repartitionHour 		= $statsDAO->calculateEntryRepartitionPerFeedPerHour($id);
		$this->view->averageHour 			= FreshRSS_StatsDAO::calculateEntryAverageFromRepartition(
			$this->view->repartitionHour
		);

		$this->view->repartitionDayOfWeek 	= $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id);
		$this->view->averageDayOfWeek 		= FreshRSS_StatsDAO::calculateEntryAverageFromRepartition(
			$this->view->repartitionDayOfWeek
		);

		$this->view->repartitionMonth 		= $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id);
		$this->view->averageMonth 			= FreshRSS_StatsDAO::calculateEntryAverageFromRepartition(
			$this->view->repartitionMonth
		);

		$hours24Labels = [];
		for ($i = 0; $i < 24; $i++) {
			$hours24Labels[$i] = $i . ':xx';
		}

		$this->view->hours24Labels = $hours24Labels;
	}

	public function unreadDatesAction(): void {
		$statsDAO = FreshRSS_Factory::createStatsDAO();
		$field = Minz_Request::paramString('field', plaintext: true);
		if (!in_array($field, ['id', 'date'], true)) {
			$field = 'id';
		}
		$granularity = Minz_Request::paramString('granularity', plaintext: true);
		if (!in_array($granularity, ['day', 'month', 'year'], true)) {
			$granularity = 'day';
		}
		$dates = $statsDAO->getMaxUnreadDates($field, $granularity, Minz_Request::paramInt('max') ?: 100,
			Minz_Request::paramIntNull('min_priority') ?? FreshRSS_Feed::PRIORITY_HIDDEN);
		$this->view->unreadDates = $dates;
	}
}
