<?php

declare(strict_types=1);
/**
 * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

namespace OCA\Calendar\Dashboard;

use DateInterval;
use DateTime;
use DateTimeImmutable;
use OCA\Calendar\AppInfo\Application;
use OCA\Calendar\Service\JSDataService;
use OCP\AppFramework\Services\IInitialState;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Calendar\IManager;
use OCP\Dashboard\IAPIWidget;
use OCP\Dashboard\IAPIWidgetV2;
use OCP\Dashboard\IButtonWidget;
use OCP\Dashboard\IIconWidget;
use OCP\Dashboard\IOptionWidget;
use OCP\Dashboard\IReloadableWidget;
use OCP\Dashboard\Model\WidgetButton;
use OCP\Dashboard\Model\WidgetItem;
use OCP\Dashboard\Model\WidgetItems;
use OCP\Dashboard\Model\WidgetOptions;
use OCP\IDateTimeFormatter;
use OCP\IL10N;
use OCP\IURLGenerator;

class CalendarWidget implements IAPIWidget, IAPIWidgetV2, IButtonWidget, IIconWidget, IOptionWidget, IReloadableWidget {
	protected IL10N $l10n;
	protected IInitialState $initialStateService;
	protected JSDataService $dataService;
	protected IDateTimeFormatter $dateTimeFormatter;
	protected IURLGenerator $urlGenerator;
	protected IManager $calendarManager;
	protected ITimeFactory $timeFactory;

	/** @var array<string, string> */
	private array $calendarDotIconCache = [];

	/**
	 * CalendarWidget constructor.
	 */
	public function __construct(IL10N $l10n,
		IInitialState $initialStateService,
		JSDataService $dataService,
		IDateTimeFormatter $dateTimeFormatter,
		IURLGenerator $urlGenerator,
		IManager $calendarManager,
		ITimeFactory $timeFactory) {
		$this->l10n = $l10n;
		$this->initialStateService = $initialStateService;
		$this->dataService = $dataService;
		$this->dateTimeFormatter = $dateTimeFormatter;
		$this->urlGenerator = $urlGenerator;
		$this->calendarManager = $calendarManager;
		$this->timeFactory = $timeFactory;
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getId(): string {
		return Application::APP_ID;
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getTitle(): string {
		return $this->l10n->t('Upcoming events');
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getOrder(): int {
		return 2;
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getIconClass(): string {
		return 'app-icon-calendar';
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getUrl(): ?string {
		return null;
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getIconUrl(): string {
		return $this->urlGenerator->getAbsoluteURL(
			$this->urlGenerator->imagePath(Application::APP_ID, 'calendar-dark.svg')
		);
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function load(): void {
		// No assets need to be loaded anymore as the widget is rendered from the API
	}

	#[\Override]
	public function getItems(string $userId, ?string $since = null, int $limit = 7): array {
		$calendars = $this->calendarManager->getCalendarsForPrincipal('principals/users/' . $userId);
		$count = count($calendars);
		if ($count === 0) {
			return [];
		}
		$dateTime = (new DateTimeImmutable())->setTimestamp($this->timeFactory->getTime());
		$inTwoWeeks = $dateTime->add(new DateInterval('P14D'));
		$options = [
			'timerange' => [
				'start' => $dateTime,
				'end' => $inTwoWeeks,
			]
		];
		$widgetItems = [];
		foreach ($calendars as $calendar) {
			if (method_exists($calendar, 'isEnabled') && $calendar->isEnabled() === false) {
				continue;
			}
			if ($calendar->isDeleted()) {
				continue;
			}
			$searchResult = $calendar->search('', [], $options, $limit);
			foreach ($searchResult as $calendarEvent) {
				// Find first recurrence in the future
				$recurrence = null;
				foreach ($calendarEvent['objects'] as $object) {
					/** @var DateTimeImmutable $startDate */
					$startDate = $object['DTSTART'][0];
					if ($startDate->getTimestamp() >= $dateTime->getTimestamp()) {
						$recurrence = $object;
						break;
					}
				}

				if ($recurrence === null) {
					continue;
				}

				$widget = new WidgetItem(
					$recurrence['SUMMARY'][0] ?? 'New Event',
					$this->dateTimeFormatter->formatTimeSpan(DateTime::createFromImmutable($startDate)),
					$this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('calendar.view.index', ['objectId' => $calendarEvent['uid']])),
					$this->getCalendarDotIconUrl($calendar->getDisplayColor()),
					(string)$startDate->getTimestamp(),
				);
				$widgetItems[] = $widget;
			}
		}

		usort($widgetItems, static function (WidgetItem $a, WidgetItem $b) {
			return (int)$a->getSinceId() - (int)$b->getSinceId();
		});

		return $widgetItems;
	}

	private function getCalendarDotIconUrl(?string $color): string {
		$sanitizedColor = ltrim(trim((string)$color), '#');
		$validColor = '#0082c9';

		if (preg_match('/^([0-9a-f]{3}|[0-9a-f]{6})$/i', $sanitizedColor) === 1) {
			$validColor = '#' . $sanitizedColor;
		}

		if (isset($this->calendarDotIconCache[$validColor])) {
			return $this->calendarDotIconCache[$validColor];
		}

		$svg = '<svg height="32" width="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="' . $validColor . '"/></svg>';
		$this->calendarDotIconCache[$validColor] = 'data:image/svg+xml,' . rawurlencode($svg);

		return $this->calendarDotIconCache[$validColor];
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getItemsV2(string $userId, ?string $since = null, int $limit = 7): WidgetItems {
		$widgetItems = $this->getItems($userId, $since, $limit);

		$halfEmptyContentMessage = '';
		if (!empty($widgetItems)) {
			$startOfTomorrow = $this->timeFactory->getDateTime('tomorrow')->getTimestamp();
			if ($widgetItems[0]->getSinceId() >= $startOfTomorrow) {
				$halfEmptyContentMessage = $this->l10n->t('No more events today');
			}
		}

		return new WidgetItems(
			$widgetItems,
			empty($widgetItems) ? $this->l10n->t('No upcoming events') : '',
			$halfEmptyContentMessage,
		);
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getWidgetButtons(string $userId): array {
		return [
			new WidgetButton(
				WidgetButton::TYPE_MORE,
				$this->urlGenerator->getAbsoluteURL(
					$this->urlGenerator->linkToRoute(Application::APP_ID . '.view.index')
				),
				$this->l10n->t('More events')
			),
		];
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getWidgetOptions(): WidgetOptions {
		return new WidgetOptions(true);
	}

	/**
	 * @inheritDoc
	 */
	#[\Override]
	public function getReloadInterval(): int {
		return 600;
	}
}
