<?php
if ( ! defined('ABSPATH') ) exit;

class HBDEV_Budget_Helpers {
	public static function month_bounds(string $ym): array {
		// $ym: "YYYY-MM"
		try {
			$start = new DateTimeImmutable($ym . '-01 00:00:00', wp_timezone());
		} catch (Exception $e) {
			$start = new DateTimeImmutable('first day of this month 00:00:00', wp_timezone());
		}
		$end = $start->modify('last day of this month 23:59:59');
		return [$start, $end];
	}

	public static function sanitize_ym(?string $ym): string {
		if (preg_match('/^\d{4}\-\d{2}$/', (string)$ym)) return $ym;
		return (new DateTimeImmutable('now', wp_timezone()))->format('Y-m');
	}

	public static function ym_prev(string $ym): string {
		[$start] = [ new DateTimeImmutable(self::sanitize_ym($ym).'-01 00:00:00', wp_timezone()) ];
		return $start->modify('-1 month')->format('Y-m');
	}

	public static function ym_next(string $ym): string {
		[$start] = [ new DateTimeImmutable(self::sanitize_ym($ym).'-01 00:00:00', wp_timezone()) ];
		return $start->modify('+1 month')->format('Y-m');
	}

	public static function stats_cache_key(string $ym, ?int $user_id = null): string {
		$ym = self::sanitize_ym($ym);
		$uid = $user_id !== null ? (int)$user_id : (int)get_current_user_id();
		return 'hbdev_budget_stats_' . $uid . '_' . $ym;
	}

	public static function invalidate_stats_cache_from(string $ym, int $months_ahead = 24, ?int $user_id = null): void {
		$ym = self::sanitize_ym($ym);
		$uid = $user_id !== null ? (int)$user_id : (int)get_current_user_id();
		$dt = new DateTimeImmutable($ym.'-01 00:00:00', wp_timezone());
		for ($i = 0; $i <= max(0, $months_ahead); $i++) {
			$key = 'hbdev_budget_stats_' . $uid . '_' . $dt->format('Y-m');
			delete_transient($key);
			$dt = $dt->modify('+1 month');
		}
	}

	/**
	 * Returns a Dashicons class for a given plugin page slug (submenu page slug).
	 * Allows configuration via filter 'hbdev_budget_dashicons'.
	 *
	 * Example:
	 * add_filter('hbdev_budget_dashicons', function($map){
	 *   $map['hbdev-budget-dashboard'] = 'dashicons-chart-pie';
	 *   $map['hbdev-active-subscriptions'] = 'dashicons-update';
	 *   return $map;
	 * });
	 */
	public static function get_page_dashicon_class(string $page_slug): string {
		$map = apply_filters('hbdev_budget_dashicons', []);
		if (!is_array($map)) $map = [];
		$icon = isset($map[$page_slug]) ? (string) $map[$page_slug] : '';
		$icon = trim($icon);
		// sanitize: only allow dashicons-* form
		if ($icon !== '' && !preg_match('/^dashicons-[a-z0-9\-]+$/', $icon)) {
			$icon = '';
		}
		return $icon;
	}
}

add_filter('hbdev_budget_dashicons', function(array $map){
	// Dashboard-Seite
	$map['hbdev-budget-dashboard'] = 'dashicons-dashboard';

	// Abos (aktiv)
	$map['hbdev-active-subscriptions'] = 'dashicons-update';

	// Abos (gekündigt/beendet)
	$map['hbdev-cancelled-subscriptions'] = 'dashicons-no';

	return $map;
});

add_filter('hbdev_budget_show_submenu_icons_in_left_nav', '__return_true');


add_action('init', function(){
	$updateUrl = 'https://www.dev-becker.de/updates/hbdev-budget/hbdev-budget.json'; // JSON-Manifest-URL
	// Auf Hauptdatei des Plugins zeigen, nicht auf diese Datei.
	$pluginFile = defined('HBDEV_BUDGET_DIR') ? HBDEV_BUDGET_DIR . 'hbdev-budget.php' : __FILE__;

	// Verwende die namespaced Factory aus "yahnis-elsts/plugin-update-checker" (Composer-Autoload erforderlich)
	if (class_exists('YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory')) {
		$checker = \YahnisElsts\PluginUpdateChecker\v5\PucFactory::buildUpdateChecker($updateUrl, $pluginFile, 'hbdev-budget');
	} elseif (class_exists('YahnisElsts\\PluginUpdateChecker\\v5p6\\PucFactory')) {
		// Fallback auf Minor-Version Namespace
		$checker = \YahnisElsts\PluginUpdateChecker\v5p6\PucFactory::buildUpdateChecker($updateUrl, $pluginFile, 'hbdev-budget');
	} else {
		// Falls Library nicht geladen ist, überspringen wir still (kein Fatal Error)
		return;
	}

	// Optional: Falls das ZIP nicht im Manifest steht, kannst du hier eine feste URL setzen.
	// $checker->setDownloadUrl('https://example.com/updates/hbdev-budget-1.2.3.zip');

	// Optional: Lizenz/Authentifizierung
	// $checker->addQueryArgFilter(function($args){
	//     $args['license_key'] = get_option('hbdev_license');
	//     return $args;
	// });

	// Changelog bevorzugt remote vom Update-Server laden (selbes Verzeichnis wie die JSON),
	// mit Fallback auf lokale Datei updates/CHANGELOG.md. Ergebnis wird gecacht.
	if (is_object($checker) && method_exists($checker, 'addResultFilter')) {
		$checker->addResultFilter(function($info, $response) use ($updateUrl) {
			$contents = '';
			// 1) Remote versuchen, falls WP HTTP API verfügbar ist
			if (function_exists('wp_remote_get') && is_string($updateUrl) && $updateUrl !== '') {
				$remoteUrl = rtrim(dirname($updateUrl), '/\\') . '/CHANGELOG.md';
				// Cache-Busting: An den Transient-Schlüssel die Metadaten-Version/last_updated anhängen, damit
				// sich der Changelog ohne Plugin-Update aktualisiert, sobald das Manifest geändert wurde.
				$freshToken = '';
				$meta = $response;
				if (is_object($meta)) {
					if (isset($meta->last_updated)) { $freshToken = (string) $meta->last_updated; }
					elseif (isset($meta->version)) { $freshToken = (string) $meta->version; }
				} elseif (is_array($meta)) {
					if (!empty($meta['last_updated'])) { $freshToken = (string) $meta['last_updated']; }
					elseif (!empty($meta['version'])) { $freshToken = (string) $meta['version']; }
				}
				$cacheKey  = 'hbdev_budget_changelog_' . md5($remoteUrl . '|' . $freshToken);
				$cached    = get_transient($cacheKey);
				if (is_string($cached) && $cached !== '') {
					$contents = $cached;
				} else {
					$httpResponse = wp_remote_get($remoteUrl, [ 'timeout' => 8, 'sslverify' => true ]);
					if (!is_wp_error($httpResponse)) {
						$code = (int) wp_remote_retrieve_response_code($httpResponse);
						$body = wp_remote_retrieve_body($httpResponse);
						if ($code === 200 && is_string($body) && $body !== '') {
							$contents = $body;
							set_transient($cacheKey, $contents, 12 * HOUR_IN_SECONDS);
						}
					}
				}
			}

			// 2) Fallback: lokale Datei im Plugin-Paket (neue Struktur: updates/hbdev-budget/CHANGELOG.md)
			if ($contents === '') {
				$changelogFile = defined('HBDEV_BUDGET_DIR')
					? HBDEV_BUDGET_DIR . 'updates/hbdev-budget/CHANGELOG.md'
					: __DIR__ . '/../updates/hbdev-budget/CHANGELOG.md';
				if (file_exists($changelogFile) && is_readable($changelogFile)) {
					$local = file_get_contents($changelogFile);
					if (is_string($local) && $local !== '') {
						$contents = $local;
					}
				}
			}

			// 3) In Plugin-Info injizieren, falls Inhalt vorhanden. Sonst unverändert lassen.
			if ($contents !== '') {
				// Leading-Whitespace/BOM entfernen und eine evtl. eigene Überschrift (H1/H2 "Changelog") am Anfang streichen,
				// damit im WP-Modal kein großer Abstand entsteht.
				if (function_exists('remove_accents')) {
					// nichts nötig, nur Beispiel falls weitere Sanitizer nötig sind
				}
				// BOM entfernen
				$contents = preg_replace('/^\xEF\xBB\xBF/', '', $contents);
				// Trimmen
				$contents = ltrim($contents);
				// Führende <h1>/<h2> mit Text "Changelog" (beliebige Attribute), case-insensitive, entfernen
				$contents = preg_replace('/^\s*<h[12][^>]*>\s*changelog\s*<\/h[12]>\s*/is', '', $contents, 1);
				// Häufige leere Platzhalter am Anfang entfernen
				$contents = preg_replace('/^(\s*<(p|div)[^>]*>\s*(?:&nbsp;|\s)*<\/\2>\s*)+/i', '', $contents, 1);

				if (!isset($info->sections) || !is_array($info->sections)) {
					$info->sections = [];
				}
				$info->sections['changelog'] = $contents;
			}
			return $info;
		});
	}
});

/**
 * Installations-ID (UUID v4) sicherstellen und zurückgeben.
 */
function hbdev_budget_get_or_create_install_id(): string {
	$install_id = get_option('hbdev_plugin_install_id');
	if (!is_string($install_id) || !preg_match('/^[0-9a-fA-F-]{36}$/', $install_id)) {
		$install_id = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : sprintf(
			'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
			random_int(0, 0xffff), random_int(0, 0xffff),
			random_int(0, 0xffff),
			random_int(0, 0x0fff) | 0x4000,
			random_int(0, 0x3fff) | 0x8000,
			random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff)
		);
		update_option('hbdev_plugin_install_id', $install_id, true);
	}
	return $install_id;
}

/**
 * Minimalen Ping an den HBDEV-Endpoint senden.
 * $event: 'install' oder 'heartbeat'
 */
function hbdev_budget_send_ping(string $event = 'heartbeat'): void {
	$install_id = hbdev_budget_get_or_create_install_id();
	$payload = [
		'plugin_slug' => 'hbdev-budget',
		'install_id'  => $install_id,
		'event'       => $event,
	];
	$args = [
		'headers' => [
			'Content-Type' => 'application/json',
			// Optional, falls Server ein Secret erwartet:
			// 'X-Client-Secret' => 'CHANGE_ME_OPTIONAL',
		],
		'body'    => wp_json_encode($payload),
		'timeout' => 5,
	];
	if (function_exists('wp_remote_post')) {
		wp_remote_post('https://dev-becker.de/active-installs/api/plugin-ping', $args);
	}
}

// Wöchentliches Intervall sicherstellen
add_filter('cron_schedules', function($schedules){
	if (!isset($schedules['weekly'])) {
		$schedules['weekly'] = [
			'interval' => 7 * DAY_IN_SECONDS,
			'display'  => __('Once Weekly', 'hbdev-budget'),
		];
	}
	return $schedules;
});

// Aktivierung/Deaktivierung an die Haupt-Plugin-Datei hängen
$__hbdev_plugin_file = defined('HBDEV_BUDGET_DIR') ? HBDEV_BUDGET_DIR . 'hbdev-budget.php' : __FILE__;

register_activation_hook($__hbdev_plugin_file, function(){
	// Install-ID erzeugen
	hbdev_budget_get_or_create_install_id();
	// Einmaligen Install-Ping senden (Fehler tolerant ignorieren)
	hbdev_budget_send_ping('install');
	// Wöchentlichen Cron registrieren
	if (!wp_next_scheduled('hbdev_budget_weekly_ping')) {
		wp_schedule_event(time() + DAY_IN_SECONDS, 'weekly', 'hbdev_budget_weekly_ping');
	}
});

register_deactivation_hook($__hbdev_plugin_file, function(){
	$ts = wp_next_scheduled('hbdev_budget_weekly_ping');
	if ($ts) wp_unschedule_event($ts, 'hbdev_budget_weekly_ping');
});

// Cron-Callback: Heartbeat 1x/Woche
add_action('hbdev_budget_weekly_ping', function(){
	hbdev_budget_send_ping('heartbeat');
});
