<?php

namespace DisableGutenberg;

use WP_PowerSuite\Core\AbstractModule;

class Module extends AbstractModule {

	private const MODULE_ID = 'disable-gutenberg';
	private const MODULE_VERSION = '1.2.0';
	private const FILTER_PRIORITY = 100;

	private ?array $settings_cache = null;

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Disable Gutenberg', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Switch posts or pages back to the classic editor while optionally keeping blocks for selected types.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return 'both';
	}

	/**
	 * @return array<string, mixed>
	 */
	public function getDefaultSettings(): array {
		return [
			'mode'                 => 'all',
			'disabled_post_types'  => [],
			'remove_frontend_css'  => true,
		];
	}

	protected function registerHooks(): void {
		$this->ensureClassicEditorCompatibility();

		add_filter( 'use_block_editor_for_post_type', [ $this, 'disableGutenbergForPostType' ], self::FILTER_PRIORITY, 2 );
		add_filter( 'use_block_editor_for_post', [ $this, 'disableGutenbergForPost' ], self::FILTER_PRIORITY, 2 );
		add_action( 'admin_print_styles', [ $this, 'safariClassicEditorFix' ] );
		add_action( 'wp_enqueue_scripts', [ $this, 'removeBlockCss' ], 999999 );
		add_action( 'wp_footer', [ $this, 'removeBlockCss' ], 999999 );
	}

	public function disableGutenbergForPostType( bool $use_block_editor, string $post_type ): bool {
		if ( ! $use_block_editor ) {
			return false;
		}

		if ( $this->isGutenbergDisabledForPostType( $post_type ) ) {
			return false;
		}

		return $use_block_editor;
	}

	/**
	 * @param bool     $use_block_editor Whether to use block editor.
	 * @param \WP_Post $post             Post object.
	 */
	public function disableGutenbergForPost( bool $use_block_editor, $post ): bool {
		if ( ! $use_block_editor ) {
			return false;
		}

		if ( ! $post || ! isset( $post->post_type ) ) {
			return $use_block_editor;
		}

		if ( $this->isGutenbergDisabledForPostType( (string) $post->post_type ) ) {
			return false;
		}

		return $use_block_editor;
	}

	public function removeBlockCss(): void {
		if ( ! $this->shouldRemoveFrontendBlockStyles() ) {
			return;
		}

		global $wp_styles;

		if ( $wp_styles instanceof \WP_Styles ) {
			foreach ( $wp_styles->queue as $handle ) {
				if ( is_string( $handle ) && str_starts_with( $handle, 'wp-block' ) ) {
					wp_dequeue_style( $handle );
					wp_deregister_style( $handle );
				}
			}
		}

		foreach ( [
			'wp-block-library',
			'wp-block-library-theme',
			'wc-blocks-style',
			'core-block-supports',
			'global-styles',
			'classic-theme-styles',
		] as $handle ) {
			wp_dequeue_style( $handle );
			wp_deregister_style( $handle );
		}
	}

	/**
	 * Temporary fix for Safari 18 negative horizontal margin on floats in classic editor.
	 *
	 * @link https://wordpress.org/support/topic/safari-18-0-breaking-classic-editor/
	 */
	public function safariClassicEditorFix(): void {
		global $current_screen;

		if ( ! isset( $current_screen->base ) || 'post' !== $current_screen->base ) {
			return;
		}

		$clear = is_rtl() ? 'right' : 'left';
		echo '<style id="wpps-classic-editor-safari-18-fix">_::-webkit-full-page-media, _:future, :root #post-body #postbox-container-2{clear:' . esc_attr( $clear ) . ';}</style>';
	}

	/**
	 * Post types that can use the block editor (for settings UI and validation).
	 *
	 * @return array<string, string> slug => label
	 */
	public static function getApplicablePostTypes(): array {
		$excluded = [
			'attachment',
			'revision',
			'nav_menu_item',
			'custom_css',
			'customize_changeset',
			'oembed_cache',
			'user_request',
			'wp_block',
			'wp_template',
			'wp_template_part',
			'wp_global_styles',
			'wp_navigation',
			'wp_font_family',
			'wp_font_face',
			'acf-post-type',
			'acf-taxonomy',
			'acf-field-group',
			'acf-ui-options-page',
			'e-landing-page',
			'elementor_library',
			'elementor_snippet',
			'elementor_font',
			'elementor_icons',
			'ct_template',
			'scheduled-action',
			'breakdance_template',
			'breakdance_header',
			'breakdance_footer',
			'breakdance_popup',
			'breakdance_block',
			'breakdance_acf_block',
			'breakdance_form_res',
			'bricks_template',
			'bricks_fonts',
		];

		$post_types = [];
		foreach ( get_post_types( [ 'public' => true ], 'objects' ) as $slug => $object ) {
			if ( in_array( $slug, $excluded, true ) ) {
				continue;
			}
			if ( ! post_type_supports( $slug, 'editor' ) ) {
				continue;
			}
			$post_types[ $slug ] = $object->labels->name ?? $slug;
		}

		asort( $post_types );

		return $post_types;
	}

	/**
	 * @param array<string, mixed> $raw Raw stored settings.
	 * @return array<string, mixed>
	 */
	public static function normalizeSettings( array $raw ): array {
		$defaults = ( new self() )->getDefaultSettings();
		$settings = array_merge( $defaults, $raw );

		// Migrate legacy allowlist (v1.1): allowed_post_types = types that keep Gutenberg.
		if ( ! isset( $raw['mode'] ) && array_key_exists( 'allowed_post_types', $raw ) ) {
			$allowed = is_array( $raw['allowed_post_types'] )
				? array_values( array_filter( array_map( 'sanitize_key', $raw['allowed_post_types'] ) ) )
				: [];

			if ( $allowed === [] ) {
				$settings['mode']                = 'all';
				$settings['disabled_post_types'] = [];
			} else {
				$all_slugs = array_keys( self::getApplicablePostTypes() );
				$settings['mode']                = 'specific';
				$settings['disabled_post_types'] = array_values( array_diff( $all_slugs, $allowed ) );
			}
		}

		$mode = isset( $settings['mode'] ) ? sanitize_key( (string) $settings['mode'] ) : 'all';
		if ( ! in_array( $mode, [ 'all', 'specific' ], true ) ) {
			$mode = 'all';
		}
		$settings['mode'] = $mode;

		$valid_slugs = array_keys( self::getApplicablePostTypes() );
		$disabled    = isset( $settings['disabled_post_types'] ) && is_array( $settings['disabled_post_types'] )
			? array_map( 'sanitize_key', $settings['disabled_post_types'] )
			: [];
		$settings['disabled_post_types'] = array_values( array_intersect( $disabled, $valid_slugs ) );

		$settings['remove_frontend_css'] = ! empty( $settings['remove_frontend_css'] );

		unset( $settings['allowed_post_types'] );

		return $settings;
	}

	private function isGutenbergDisabledForPostType( string $post_type ): bool {
		$settings = $this->getSettings();

		if ( 'all' === $settings['mode'] ) {
			return true;
		}

		return in_array( $post_type, $settings['disabled_post_types'], true );
	}

	private function shouldRemoveFrontendBlockStyles(): bool {
		$settings = $this->getSettings();

		if ( empty( $settings['remove_frontend_css'] ) ) {
			return false;
		}

		if ( 'all' === $settings['mode'] ) {
			return true;
		}

		global $post;

		if ( is_numeric( $post ) ) {
			$post = get_post( (int) $post );
		}

		if ( ! is_object( $post ) || ! isset( $post->post_type ) ) {
			return false;
		}

		return $this->isGutenbergDisabledForPostType( (string) $post->post_type );
	}

	private function ensureClassicEditorCompatibility(): void {
		if ( class_exists( 'Classic_Editor', false ) ) {
			return;
		}

		$shim = __DIR__ . '/includes/classic-editor-shim.php';
		if ( is_readable( $shim ) ) {
			require_once $shim;
		}
	}

	/**
	 * @return array<string, mixed>
	 */
	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}

		$raw = $this->moduleSettings()->all( [] );
		if ( ! is_array( $raw ) ) {
			$raw = [];
		}

		$this->settings_cache = self::normalizeSettings( $raw );

		return $this->settings_cache;
	}
}
