<?php

namespace Fliegerlogin;

class Options {

	private static $option_dbVersion = 'WpVereinsflieger_DbVersion';

	private static $option_appKey = 'WpVereinsflieger_AppKey';
	private static $option_cid = 'WpVereinsflieger_CID';

	private static $option_defaultRole = 'WpVereinsflieger_DefaultRole';

	private static $option_allowDefaultLogin = 'WpVereinsflieger_AllowDefaultLogin';

	private static $option_rateLimitEnabled        = 'WpVereinsflieger_RateLimitEnabled';
	private static $option_rateLimitAllLogins      = 'WpVereinsflieger_RateLimitAllLogins';
	private static $option_rateLimitMaxAttempts    = 'WpVereinsflieger_RateLimitMaxAttempts';
	private static $option_rateLimitLockoutSeconds = 'WpVereinsflieger_RateLimitLockoutSeconds';
	private static $option_rateLimitBlockedIps     = 'WpVereinsflieger_RateLimitBlockedIps';
	private static $option_rateLimitEvents         = 'WpVereinsflieger_RateLimitEvents';

	public const RATE_LIMIT_EVENT_LIMIT = 50;

	/**
	 * Removes all options previously set by the class
	 */
	public static function uninstall() {
		delete_option(self::$option_dbVersion);
		delete_option(self::$option_appKey);
		delete_option(self::$option_cid);
		delete_option(self::$option_defaultRole);
		delete_option(self::$option_allowDefaultLogin);
		delete_option(self::$option_rateLimitEnabled);
		delete_option(self::$option_rateLimitAllLogins);
		delete_option(self::$option_rateLimitMaxAttempts);
		delete_option(self::$option_rateLimitLockoutSeconds);
		delete_option(self::$option_rateLimitBlockedIps);
		delete_option(self::$option_rateLimitEvents);
	}

	/**
	 * Returns the db version of this plugin
	 * @return int|null
	 */
	public static function getInstalledDbVersion(): ?int {
		return get_option(self::$option_dbVersion, null);
	}

	/**
	 * Sets the db version of this plugin
	 * @param int $dbVersion
	 */
	public static function setInstalledDbVersion(int $dbVersion): void {
		if (self::getInstalledDbVersion() === null) {
			add_option(self::$option_dbVersion, $dbVersion, '', false);
		} else {
			update_option(self::$option_dbVersion, $dbVersion);
		}
	}

	/**
	 * Returns the app key needed for the Vereinsflieger.de rest api
	 * @return string|null
	 */
	public static function getAppKey(): ?string {
		return get_option(self::$option_appKey, null);
	}

	/**
	 * Sets the app key needed for the Vereinsflieger.de rest api
	 * @param string $appKey
	 */
	public static function setAppKey(string $appKey): void {
		if (self::getAppKey() === null) {
			add_option(self::$option_appKey, $appKey, '', false);
		} else {
			update_option(self::$option_appKey, $appKey);
		}
	}

	/**
	 * Returns the club id the api should connect to
	 * @return int|null
	 */
	public static function getCID(): ?int {
		$value = get_option(self::$option_cid, null);
		return $value === null ? null : (int) $value;
	}

	/**
	 * Sets the club id the api should connect to
	 * @param int $cid
	 */
	public static function setCID(int $cid): void {
		if (self::getCID() === null) {
			add_option(self::$option_cid, $cid, '', false);
		} else {
			update_option(self::$option_cid, $cid);
		}
	}

	/**
	 * Returns the role a new user is added to
	 * @return string|null
	 */
	public static function getDefaultRole(): ?string {
		return get_option(self::$option_defaultRole, null);
	}

	/**
	 * Sets the role a new user is added to
	 * @param string $role
	 */
	public static function setDefaultRole(string $role): void {
		if (self::getDefaultRole() === null) {
			add_option(self::$option_defaultRole, $role, '', false);
		} else {
			update_option(self::$option_defaultRole, $role);
		}
	}

	/**
	 * Returns if a user linked to a vereinsflieger account should be able to also login with his wordpress credentials.
	 * Default is false.
	 * @return bool
	 */
	public static function getAllowDefaultLogin(): bool {
		return get_option(self::$option_allowDefaultLogin, false);
	}

	/**
	 * Sets if an account linked to a vereinsflieger user should be able to login using it's wordpress credentials
	 * @param bool $isAllowed
	 */
	public static function setAllowDefaultLogin(bool $isAllowed): void {
		if (get_option(self::$option_allowDefaultLogin, null) === null) {
			add_option(self::$option_allowDefaultLogin, $isAllowed, '', false);
		} else {
			update_option(self::$option_allowDefaultLogin, $isAllowed);
		}
	}

	/**
	 * Checks if the plugin is set up correctly.
	 * This means only that all required options are set.
	 * @return bool
	 */
	public static function setupComplete(): bool {
		return !(empty(Options::getAppKey()) || empty(Options::getCID()));
	}

	public static function isRateLimitEnabled(): bool {
		return (bool) get_option(self::$option_rateLimitEnabled, true);
	}

	public static function setRateLimitEnabled(bool $enabled): void {
		self::setOption(self::$option_rateLimitEnabled, $enabled);
	}

	public static function isRateLimitAllLoginsEnabled(): bool {
		return (bool) get_option(self::$option_rateLimitAllLogins, false);
	}

	public static function setRateLimitAllLoginsEnabled(bool $enabled): void {
		self::setOption(self::$option_rateLimitAllLogins, $enabled);
	}

	public static function getRateLimitMaxAttempts(): int {
		return max(1, (int) get_option(self::$option_rateLimitMaxAttempts, 5));
	}

	public static function setRateLimitMaxAttempts(int $maxAttempts): void {
		self::setOption(self::$option_rateLimitMaxAttempts, max(1, $maxAttempts));
	}

	public static function getRateLimitLockoutSeconds(): int {
		return max(60, (int) get_option(self::$option_rateLimitLockoutSeconds, 900));
	}

	public static function setRateLimitLockoutSeconds(int $lockoutSeconds): void {
		self::setOption(self::$option_rateLimitLockoutSeconds, max(60, $lockoutSeconds));
	}

	/**
	 * @return array<string, array{ip:string, attempts:int, first_failed_at:int, last_failed_at:int, blocked_until:int|null}>
	 */
	public static function getRateLimitBlockedIps(): array {
		$blockedIps = get_option(self::$option_rateLimitBlockedIps, []);
		return is_array($blockedIps) ? $blockedIps : [];
	}

	public static function setRateLimitBlockedIps(array $blockedIps): void {
		self::setOption(self::$option_rateLimitBlockedIps, $blockedIps);
	}

	/**
	 * @return array<int, array{ts:float, ip:string, attempts:int}>
	 */
	public static function getRateLimitEvents(): array {
		$events = get_option(self::$option_rateLimitEvents, []);
		return is_array($events) ? $events : [];
	}

	public static function appendRateLimitEvent(array $entry): void {
		$events = self::getRateLimitEvents();
		$events[] = $entry;
		if (count($events) > self::RATE_LIMIT_EVENT_LIMIT) {
			$events = array_slice($events, -self::RATE_LIMIT_EVENT_LIMIT);
		}
		self::setOption(self::$option_rateLimitEvents, $events);
	}

	private static function setOption(string $option, $value): void {
		if (get_option($option, null) === null) {
			add_option($option, $value, '', false);
		} else {
			update_option($option, $value);
		}
	}

}
