<?php

declare( strict_types=1 );

namespace StrykeWP\Core;

/**
 * Checks for plugin updates from the STRYKE Hub API.
 *
 * Hooks into WordPress's native update system so updates appear
 * alongside wp.org plugin updates in Dashboard → Updates.
 *
 * Remote endpoint returns JSON:
 * {
 *   "name":         "STRYKE WP",
 *   "slug":         "stryke-wp",
 *   "version":      "1.0.1",
 *   "requires":     "6.0",
 *   "requires_php": "8.0",
 *   "tested":       "6.7",
 *   "download_url": "https://…/stryke-wp-1.0.1.zip",
 *   "changelog":    "<h4>1.0.1</h4>…",
 *   "last_updated": "2026-03-10",
 *   "author":       "STRYKE Marketing",
 *   "homepage":     "https://www.strykemarketing.de"
 * }
 *
 * @package StrykeWP\Core
 */
final class UpdateChecker {

	/**
	 * Remote API endpoint that returns current version info.
	 */
	private const API_URL = 'https://updates.strykehub.com/api/wp-plugin/info';

	/**
	 * Transient key for caching the remote response.
	 */
	private const CACHE_KEY = 'stryke_wp_update_data';

	/**
	 * Cache duration in seconds (12 hours).
	 */
	private const CACHE_TTL = 43200;

	public function __construct(
		private readonly AssetManager $assets,
	) {}

	/**
	 * Registers WordPress hooks for the update system.
	 */
	public function register_hooks(): void {
		// Inject update info into the update_plugins transient.
		add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_for_update' ] );

		// Provide full plugin info for the "View details" modal.
		add_filter( 'plugins_api', [ $this, 'plugin_info' ], 20, 3 );

		// Clear cache when WordPress force-checks for updates.
		add_action( 'upgrader_process_complete', [ $this, 'clear_cache' ], 10, 0 );

		// Force auto-update for STRYKE WP — always update when a new version is detected.
		add_filter( 'auto_update_plugin', [ $this, 'force_auto_update' ], 10, 2 );
	}

	/**
	 * Forces WordPress to auto-update this plugin when a new version is available.
	 *
	 * @param bool|null $update Whether to auto-update.
	 * @param object    $item   Plugin update data.
	 * @return bool|null
	 */
	public function force_auto_update( mixed $update, object $item ): mixed {
		if ( isset( $item->slug ) && 'stryke-wp' === $item->slug ) {
			return true;
		}
		return $update;
	}

	// -------------------------------------------------------------------------
	// Update transient
	// -------------------------------------------------------------------------

	/**
	 * Checks the remote API and injects update data into the WP transient.
	 *
	 * @param object|mixed $transient The update_plugins transient value.
	 * @return object|mixed
	 */
	public function check_for_update( mixed $transient ): mixed {
		if ( ! is_object( $transient ) ) {
			return $transient;
		}

		$remote = $this->get_remote_data();

		if ( ! $remote ) {
			return $transient;
		}

		$update_available = version_compare(
			$remote->version,
			STRYKE_WP_VERSION,
			'>'
		);

		if ( $update_available ) {
			$transient->response[ STRYKE_WP_BASENAME ] = (object) [
				'slug'         => $remote->slug,
				'plugin'       => STRYKE_WP_BASENAME,
				'new_version'  => $remote->version,
				'url'          => $remote->homepage ?? 'https://www.strykemarketing.de',
				'package'      => $remote->download_link ?? $remote->download_url ?? '',
				'icons'        => $this->get_icons(),
				'banners'      => $this->get_banners(),
				'requires'     => $remote->requires ?? '6.0',
				'requires_php' => $remote->requires_php ?? '8.0',
				'tested'       => $remote->tested ?? '',
			];
		} else {
			// Tell WP that we checked and there is no update — prevents
			// WP from querying wp.org for our non-listed slug.
			$transient->no_update[ STRYKE_WP_BASENAME ] = (object) [
				'slug'         => 'stryke-wp',
				'plugin'       => STRYKE_WP_BASENAME,
				'new_version'  => STRYKE_WP_VERSION,
				'url'          => 'https://www.strykemarketing.de',
				'package'      => '',
				'icons'        => $this->get_icons(),
				'banners'      => $this->get_banners(),
				'requires'     => $remote->requires ?? '6.0',
				'requires_php' => $remote->requires_php ?? '8.0',
				'tested'       => $remote->tested ?? '',
			];
		}

		return $transient;
	}

	// -------------------------------------------------------------------------
	// Plugin info modal ("View details")
	// -------------------------------------------------------------------------

	/**
	 * Provides full plugin information for the WP "View details" modal.
	 *
	 * @param false|object|array<string, mixed> $result Default result.
	 * @param string                            $action API action.
	 * @param object                            $args   Request arguments.
	 * @return false|object|array<string, mixed>
	 */
	public function plugin_info( mixed $result, string $action, object $args ): mixed {
		if ( 'plugin_information' !== $action ) {
			return $result;
		}

		if ( ! isset( $args->slug ) || 'stryke-wp' !== $args->slug ) {
			return $result;
		}

		$remote = $this->get_remote_data();

		if ( ! $remote ) {
			return $result;
		}

		// Server may provide sections as object { description, changelog }
		// or as flat fields. Handle both.
		$sections = isset( $remote->sections ) && is_object( $remote->sections )
			? [
				'description' => $remote->sections->description ?? '',
				'changelog'   => $remote->sections->changelog ?? '',
			]
			: [
				'description' => $remote->description ?? '',
				'changelog'   => $remote->changelog ?? '',
			];

		return (object) [
			'name'          => $remote->name ?? 'STRYKE WP',
			'slug'          => $remote->slug ?? 'stryke-wp',
			'version'       => $remote->version,
			'author'        => sprintf(
				'<a href="%s">%s</a>',
				esc_url( $remote->homepage ?? 'https://www.strykemarketing.de' ),
				esc_html( $remote->author ?? 'STRYKE Marketing' )
			),
			'homepage'      => $remote->homepage ?? 'https://www.strykemarketing.de',
			'requires'      => $remote->requires ?? '6.0',
			'requires_php'  => $remote->requires_php ?? '8.0',
			'tested'        => $remote->tested ?? '',
			'downloaded'    => $remote->downloaded ?? 0,
			'last_updated'  => $remote->last_updated ?? '',
			'sections'      => $sections,
			'download_link' => $remote->download_link ?? $remote->download_url ?? '',
			'icons'         => $this->get_icons(),
			'banners'       => $this->get_banners(),
		];
	}

	// -------------------------------------------------------------------------
	// Cache
	// -------------------------------------------------------------------------

	/**
	 * Clears the cached remote data (e.g. after an upgrade completes).
	 */
	public function clear_cache(): void {
		delete_transient( self::CACHE_KEY );
	}

	// -------------------------------------------------------------------------
	// Remote data
	// -------------------------------------------------------------------------

	/**
	 * Fetches and caches the remote version info.
	 *
	 * @return object|null Decoded JSON object or null on failure.
	 */
	private function get_remote_data(): ?object {
		$cached = get_transient( self::CACHE_KEY );

		if ( false !== $cached && is_object( $cached ) ) {
			return $cached;
		}

		$response = wp_remote_get(
			self::API_URL,
			[
				'timeout' => 10,
				'headers' => [
					'Accept' => 'application/json',
				],
			]
		);

		if ( is_wp_error( $response ) ) {
			return null;
		}

		$code = wp_remote_retrieve_response_code( $response );

		if ( 200 !== $code ) {
			return null;
		}

		$body = wp_remote_retrieve_body( $response );
		$data = json_decode( $body );

		if ( ! is_object( $data ) || empty( $data->version ) ) {
			return null;
		}

		set_transient( self::CACHE_KEY, $data, self::CACHE_TTL );

		return $data;
	}

	// -------------------------------------------------------------------------
	// Icons & Banners
	// -------------------------------------------------------------------------

	/**
	 * Returns plugin icon URLs for WP update UI.
	 *
	 * @return array<string, string>
	 */
	private function get_icons(): array {
		$img_url = $this->assets->asset_url( 'img/' );

		return [
			'svg'     => $img_url . 'stryke_icon.svg',
			'1x'      => $img_url . 'icon-128x128.png',
			'2x'      => $img_url . 'icon-256x256.png',
			'default' => $img_url . 'icon-256x256.png',
		];
	}

	/**
	 * Returns plugin banner URLs for WP update UI.
	 *
	 * @return array<string, string>
	 */
	private function get_banners(): array {
		$img_url = $this->assets->asset_url( 'img/' );

		return [
			'low'  => $img_url . 'banner-772x250.png',
			'high' => $img_url . 'banner-1544x500.png',
		];
	}
}
