<?php
declare(strict_types=1);

namespace Wohno\Plugin;

use Wohno\Plugin\Admin\Feedback;
use Wohno\Plugin\Ajax\ListingDetail;
use Wohno\Plugin\Ajax\LoadMore;
use Wohno\Plugin\Ajax\Preview;
use Wohno\Plugin\Api\Client;
use Wohno\Plugin\Block\ListingsBlock;
use Wohno\Plugin\Block\Patterns;
use Wohno\Plugin\Block\SingleListingBlock;
use Wohno\Plugin\Cron\Scheduler;
use Wohno\Plugin\Support\Privacy;
use Wohno\Plugin\Render\ListingsRenderer;
use Wohno\Plugin\Settings\SettingsPage;
use Wohno\Plugin\Shortcode\ListingShortcode;
use Wohno\Plugin\Shortcode\ListingsShortcode;
use Wohno\Plugin\Widget\ListingsWidget;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Plugin orchestrator — wires settings, shortcode, widget and block.
 *
 * Singleton so WordPress hooks register exactly once.
 */
final class Plugin {

	/**
	 * Bundled Leaflet version (asset cache-buster).
	 *
	 * @var string
	 */
	private const LEAFLET_VERSION = '1.9.4';

	/**
	 * The single shared instance.
	 *
	 * @var Plugin|null
	 */
	private static ?Plugin $instance = null;

	/**
	 * Whether boot() has already run (hooks registered).
	 *
	 * @var bool
	 */
	private bool $booted = false;

	/**
	 * Get (or lazily create) the shared plugin instance.
	 *
	 * @return Plugin
	 */
	public static function instance(): Plugin {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Private constructor — use {@see Plugin::instance()}.
	 */
	private function __construct() {}

	/**
	 * Register all WordPress hooks. Idempotent.
	 *
	 * @return void
	 */
	public function boot(): void {
		if ( $this->booted ) {
			return;
		}
		$this->booted = true;

		// No load_plugin_textdomain(): since WP 4.6 translations for
		// wordpress.org-hosted plugins are loaded automatically by core.

		$client    = new Client();
		$renderer  = new ListingsRenderer();
		$shortcode = new ListingsShortcode( $client, $renderer );

		( new SettingsPage( $client ) )->register();
		$shortcode->register();
		( new ListingShortcode( $client, $renderer ) )->register();
		( new ListingsBlock( $client, $renderer ) )->register();
		( new SingleListingBlock( $client, $renderer ) )->register();
		( new LoadMore( $client, $renderer ) )->register();
		( new ListingDetail( $client ) )->register();
		( new Preview( $shortcode, $renderer ) )->register();
		( new Patterns() )->register();
		( new Privacy() )->register();
		( new Feedback() )->register();
		( new Scheduler( $client ) )->register();

		add_action(
			'widgets_init',
			static function () use ( $client, $renderer ): void {
				register_widget( new ListingsWidget( $client, $renderer ) );
			}
		);

		add_action( 'init', array( $this, 'register_assets' ) );
	}

	/**
	 * Build scoped CSS-variable overrides from the admin display settings.
	 *
	 * Only emits a custom property when it actually differs from the stylesheet
	 * default, so the bundled theme is untouched unless the makler customises it.
	 *
	 * @return string CSS (without a `<style>` wrapper), or '' when nothing to override.
	 */
	private static function frontend_overrides_css(): string {
		$vars = '';

		$accent = sanitize_hex_color( (string) get_option( 'wohno_accent_color', '' ) );
		if ( is_string( $accent ) && '' !== $accent && '#df6900' !== strtolower( $accent ) ) {
			$vars .= '--wohno-accent:' . $accent . ';--wohno-accent-strong:' . self::darken( $accent, 0.82 ) . ';';
		}

		$radius = (string) get_option( 'wohno_card_radius', '' );
		if ( in_array( $radius, array( '0px', '8px', '14px', '26px' ), true ) ) {
			$vars .= '--wohno-radius:' . $radius . ';';
		}

		return '' === $vars
			? ''
			: '.wohno-listings,.wohno-single,.wohno-map,.wohno-listings-wrap{' . $vars . '}';
	}

	/**
	 * Darken a `#rrggbb` colour by a multiplicative factor (0–1).
	 *
	 * @param string $hex    A validated `#rrggbb` colour.
	 * @param float  $factor Channel multiplier (e.g. 0.82 = 18% darker).
	 * @return string The darkened `#rrggbb` colour.
	 */
	private static function darken( string $hex, float $factor ): string {
		$hex = ltrim( $hex, '#' );
		if ( 3 === strlen( $hex ) ) {
			$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
		}
		if ( 6 !== strlen( $hex ) ) {
			return '#' . $hex;
		}

		$r = (int) max( 0, min( 255, round( hexdec( substr( $hex, 0, 2 ) ) * $factor ) ) );
		$g = (int) max( 0, min( 255, round( hexdec( substr( $hex, 2, 2 ) ) * $factor ) ) );
		$b = (int) max( 0, min( 255, round( hexdec( substr( $hex, 4, 2 ) ) * $factor ) ) );

		return sprintf( '#%02x%02x%02x', $r, $g, $b );
	}

	/**
	 * Register the lightweight frontend stylesheet (enqueued on demand by
	 * the renderer, on both the frontend and the block-editor preview).
	 *
	 * @return void
	 */
	public function register_assets(): void {
		wp_register_style(
			'wohno-listings',
			WOHNO_PLUGIN_URL . 'assets/css/listings.css',
			array(),
			VERSION
		);

		// Apply the admin-configured accent colour / corner radius as scoped
		// CSS-variable overrides (emitted only when they differ from the defaults).
		$overrides = self::frontend_overrides_css();
		if ( '' !== $overrides ) {
			wp_add_inline_style( 'wohno-listings', $overrides );
		}

		// Card gallery + detail modal — enqueued on demand by the renderer.
		wp_register_script(
			'wohno-listings',
			WOHNO_PLUGIN_URL . 'assets/js/listings.js',
			array(),
			VERSION,
			true
		);
		wp_localize_script(
			'wohno-listings',
			'wohnoListings',
			array(
				'ajaxUrl'          => admin_url( 'admin-ajax.php' ),
				'detailAction'     => ListingDetail::ACTION,
				'detailNonce'      => wp_create_nonce( ListingDetail::NONCE ),
				'close'            => __( 'Schließen', 'wohno' ),
				'prev'             => __( 'Zurück', 'wohno' ),
				'next'             => __( 'Weiter', 'wohno' ),
				'view'             => __( 'Vollständig ansehen', 'wohno' ),
				'apply'            => __( 'Jetzt bewerben', 'wohno' ),
				'rooms'            => __( 'Zimmer', 'wohno' ),
				'bedrooms'         => __( 'Schlafzimmer', 'wohno' ),
				'bathrooms'        => __( 'Badezimmer', 'wohno' ),
				'area'             => __( 'Wohnfläche', 'wohno' ),
				'type'             => __( 'Objekttyp', 'wohno' ),
				'available'        => __( 'Verfügbar ab', 'wohno' ),
				'descriptionTitle' => __( 'Beschreibung', 'wohno' ),
				'costsTitle'       => __( 'Kosten', 'wohno' ),
			)
		);

		// "Load more" script — enqueued on demand by the renderer. The nonce is
		// localised here (fresh per request) so it never gets baked into the
		// cached grid HTML.
		wp_register_script(
			'wohno-load-more',
			WOHNO_PLUGIN_URL . 'assets/js/load-more.js',
			array(),
			VERSION,
			true
		);
		wp_localize_script(
			'wohno-load-more',
			'wohnoLoadMore',
			array(
				'ajaxUrl' => admin_url( 'admin-ajax.php' ),
				'action'  => LoadMore::ACTION,
				'nonce'   => wp_create_nonce( LoadMore::NONCE ),
			)
		);

		// Map layout: bundled Leaflet (keyless OpenStreetMap tiles) + init script.
		wp_register_style(
			'wohno-leaflet',
			WOHNO_PLUGIN_URL . 'assets/vendor/leaflet/leaflet.css',
			array(),
			self::LEAFLET_VERSION
		);
		// Ship both the minified build and its source (leaflet-src.js); serve the
		// readable source when SCRIPT_DEBUG is on (and so the source is present in
		// the package, as wp.org requires for bundled minified libraries).
		$leaflet_file = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? 'leaflet-src.js' : 'leaflet.js';
		wp_register_script(
			'wohno-leaflet-js',
			WOHNO_PLUGIN_URL . 'assets/vendor/leaflet/' . $leaflet_file,
			array(),
			self::LEAFLET_VERSION,
			true
		);
		wp_register_script(
			'wohno-map',
			WOHNO_PLUGIN_URL . 'assets/js/map.js',
			array( 'wohno-leaflet-js' ),
			VERSION,
			true
		);
	}
}
