<?php
/**
 * AI Summarize — Pro content module (shortcode UI for third-party AI chats).
 *
 * @package AISummarize
 */

namespace AISummarize;

use WP_PowerSuite\Core\AbstractModule;

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

require_once __DIR__ . '/includes/class-provider-registry.php';
require_once __DIR__ . '/includes/class-url-helper.php';

class Module extends AbstractModule {

	private const MODULE_ID      = 'ai-summarize';
	private const MODULE_VERSION = '1.0.0';
	private const SHORTCODE      = 'wpps_ai_summarize';

	/** @var int */
	private static int $instance_seq = 0;

	/** @var bool */
	private static bool $assets_enqueued = false;

	/** @var array<string, mixed>|null */
	private ?array $settings_cache = null;

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

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

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

	public function getDescription(): string {
		return __( 'Add a “Summarize with AI” shortcode that opens ChatGPT, Gemini, Claude, Perplexity, or Grok with a safe page-URL prompt—no API keys or on-site AI calls.', 'wp-powersuite' );
	}

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

	public function getDependencies(): array {
		return [];
	}

	private function isLicenseActive(): bool {
		return function_exists( 'wp_powersuite_is_license_valid' ) && wp_powersuite_is_license_valid();
	}

	protected function registerHooks(): void {
		if ( ! $this->isLicenseActive() ) {
			return;
		}

		add_shortcode( self::SHORTCODE, [ $this, 'renderShortcode' ] );
		add_action( 'wp_enqueue_scripts', [ $this, 'maybeEnqueueAssetsEarly' ], 20 );
	}

	public function maybeEnqueueAssetsEarly(): void {
		if ( is_admin() || ! is_singular() ) {
			return;
		}

		$post = get_post();
		if ( ! $post instanceof \WP_Post ) {
			return;
		}

		$raw = (string) $post->post_content;
		if ( ! has_shortcode( $raw, self::SHORTCODE ) && ! preg_match( '/\[\s*wpps_ai_summarize\b/i', $raw ) ) {
			return;
		}

		$this->enqueueFrontendAssets();
	}

	/**
	 * @param array<string, string>|string $atts
	 */
	public function renderShortcode( $atts = [] ): string {
		if ( ! $this->isLicenseActive() || ! $this->isEnabled() ) {
			return '';
		}

		$settings = $this->getSettings();

		/**
		 * @param array<string, mixed> $atts
		 * @param array<string, mixed> $settings
		 */
		$atts = apply_filters( 'wpps_ai_summarize_shortcode_atts', shortcode_atts( $this->shortcodeDefaults(), is_array( $atts ) ? $atts : [], self::SHORTCODE ), $settings );
		if ( ! is_array( $atts ) ) {
			$atts = $this->shortcodeDefaults();
		}

		$merged = $this->mergeShortcodeOverrides( $settings, $atts );

		/**
		 * @param bool                 $allow
		 * @param array<string, mixed> $merged
		 */
		$allow = apply_filters( 'wpps_ai_summarize_can_render', $this->canRender( $merged ), $merged );
		if ( ! $allow ) {
			return '';
		}

		$providers = $this->resolveProvidersForRender( $merged, $atts );
		if ( $providers === [] ) {
			return '';
		}

		$this->enqueueFrontendAssets();

		return $this->renderComponent( $merged, $providers );
	}

	/**
	 * @param array<string, mixed>       $settings
	 * @param list<array<string, mixed>> $providers
	 */
	private function renderComponent( array $settings, array $providers ): string {
		self::$instance_seq++;
		$uid = 'wpps-ais-' . (string) self::$instance_seq;

		$url    = UrlHelper::getShareableUrl( $settings );
		$prompt = UrlHelper::buildPrompt( $settings, $url );

		$alignment    = (string) ( $settings['alignment'] ?? 'left' );
		$button_style = (string) ( $settings['button_style'] ?? 'icon_label' );
		$shape        = (string) ( $settings['button_shape'] ?? 'rounded' );
		$size         = (string) ( $settings['button_size'] ?? 'medium' );
		$show_heading = ! empty( $settings['show_heading'] );
		$heading      = (string) ( $settings['heading_text'] ?? '' );
		if ( $heading === '' ) {
			$heading = __( 'Summarize with AI:', 'wp-powersuite' );
		}

		$open_new_tab = ! empty( $settings['open_new_tab'] );
		$also_copy    = ! empty( $settings['copy_prompt_before_opening'] );

		$classes = [
			'wpps-ai-summarize',
			'wpps-ai-summarize--align-' . sanitize_html_class( $alignment ),
			'wpps-ai-summarize--style-' . sanitize_html_class( $button_style ),
			'wpps-ai-summarize--shape-' . sanitize_html_class( $shape ),
			'wpps-ai-summarize--size-' . sanitize_html_class( $size ),
		];

		/**
		 * @param list<string>         $classes
		 * @param array<string, mixed> $settings
		 */
		$classes = apply_filters( 'wpps_ai_summarize_wrapper_classes', $classes, $settings );
		if ( ! is_array( $classes ) ) {
			$classes = [ 'wpps-ai-summarize' ];
		}

		$target = $open_new_tab ? '_blank' : '_self';
		$rel    = $open_new_tab ? 'noopener noreferrer' : '';

		$out  = '<div class="' . esc_attr( implode( ' ', array_map( 'sanitize_html_class', $classes ) ) ) . '"';
		$out .= ' data-wpps-ai-summarize="1"';
		$out .= ' role="group"';
		$out .= ' aria-label="' . esc_attr__( 'Summarize this page with AI', 'wp-powersuite' ) . '"';
		$out .= '>';

		if ( $show_heading ) {
			$out .= '<p class="wpps-ai-summarize__heading">' . esc_html( $heading ) . '</p>';
		}

		$out .= '<div class="wpps-ai-summarize__buttons">';

		$needs_any_clipboard = false;
		foreach ( $providers as $provider ) {
			$slug = sanitize_key( (string) ( $provider['slug'] ?? '' ) );
			$name = (string) ( $provider['name'] ?? $slug );
			/* translators: %s: AI provider name */
			$label = sprintf( __( 'Summarize with %s', 'wp-powersuite' ), $name );

			$supports_prefill = ! empty( $provider['prefill_supported'] );
			$launch_url       = ProviderRegistry::buildLaunchUrl( $provider, $supports_prefill ? $prompt : '' );
			if ( $launch_url === '' ) {
				$launch_url = ProviderRegistry::sanitizeHttpsUrl( (string) ( $provider['url'] ?? '' ) );
			}

			// Copy when requested, or when the provider cannot prefill via URL.
			$needs_clipboard = $also_copy || ! $supports_prefill;
			if ( $needs_clipboard ) {
				$needs_any_clipboard = true;
			}

			$icon_key = sanitize_key( (string) ( $provider['icon'] ?? 'sparkle' ) );
			$icon_svg = ProviderRegistry::getIconSvg( $icon_key );

			$inner = '';
			if ( $button_style !== 'label' ) {
				$inner .= '<span class="wpps-ai-summarize__icon" aria-hidden="true">' . $icon_svg . '</span>';
			}
			if ( $button_style !== 'icon' ) {
				$inner .= '<span class="wpps-ai-summarize__label">' . esc_html( $name ) . '</span>';
			}

			$classes_btn = 'wpps-ai-summarize__btn wpps-ai-summarize__btn--' . sanitize_html_class( $slug );
			if ( $needs_clipboard ) {
				$classes_btn .= ' wpps-ai-summarize__btn--clipboard';
			}

			$out .= '<a class="' . esc_attr( $classes_btn ) . '"';
			$out .= ' href="' . esc_url( $launch_url ) . '"';
			$out .= ' target="' . esc_attr( $target ) . '"';
			if ( $rel !== '' ) {
				$out .= ' rel="' . esc_attr( $rel ) . '"';
			}
			$out .= ' aria-label="' . esc_attr( $label ) . '"';
			$out .= ' data-provider="' . esc_attr( $slug ) . '"';
			$out .= ' data-prompt="' . esc_attr( $prompt ) . '"';
			$out .= ' data-clipboard="' . ( $needs_clipboard ? '1' : '0' ) . '"';
			$out .= ' data-open-url="' . esc_url( $launch_url ) . '"';
			$out .= '>' . $inner . '</a>';
		}

		$out .= '</div>';
		$out .= '<div class="wpps-ai-summarize__status" role="status" aria-live="polite" aria-atomic="true"></div>';

		// Only output the clipboard fallback UI when a provider may need it (avoids TOC picking up dialog text).
		if ( $needs_any_clipboard ) {
			$out .= '<div class="wpps-ai-summarize__dialog" hidden data-dialog="1">';
			$out .= '<div class="wpps-ai-summarize__dialog-backdrop" data-dialog-close="1" tabindex="-1"></div>';
			$out .= '<div class="wpps-ai-summarize__dialog-panel" role="dialog" aria-modal="true" aria-labelledby="' . esc_attr( $uid . '-dialog-title' ) . '">';
			// Use <p>, not a heading — TOC plugins often scrape h1–h6 even from hidden markup.
			$out .= '<p id="' . esc_attr( $uid . '-dialog-title' ) . '" class="wpps-ai-summarize__dialog-title">' . esc_html__( 'Copy prompt', 'wp-powersuite' ) . '</p>';
			$out .= '<p class="wpps-ai-summarize__dialog-help">' . esc_html__( 'Clipboard access was blocked. Copy the prompt below, then paste it into the AI chat.', 'wp-powersuite' ) . '</p>';
			$out .= '<textarea class="wpps-ai-summarize__dialog-text" readonly rows="6">' . esc_textarea( $prompt ) . '</textarea>';
			$out .= '<div class="wpps-ai-summarize__dialog-actions">';
			$out .= '<button type="button" class="wpps-ai-summarize__dialog-copy">' . esc_html__( 'Select & copy', 'wp-powersuite' ) . '</button>';
			$out .= '<button type="button" class="wpps-ai-summarize__dialog-close" data-dialog-close="1">' . esc_html__( 'Close', 'wp-powersuite' ) . '</button>';
			$out .= '</div></div></div>';
		}

		$out .= '</div>';

		/**
		 * @param string               $html
		 * @param array<string, mixed> $settings
		 */
		$filtered = apply_filters( 'wpps_ai_summarize_html', $out, $settings );

		return is_string( $filtered ) ? $filtered : $out;
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function canRender( array $settings ): bool {
		if ( is_admin() && ! wp_doing_ajax() ) {
			return false;
		}

		if ( ! empty( $settings['disable_logged_out'] ) && ! is_user_logged_in() ) {
			return false;
		}
		if ( ! empty( $settings['disable_logged_in'] ) && is_user_logged_in() ) {
			return false;
		}

		if ( UrlHelper::isSensitiveContext() ) {
			return false;
		}

		return true;
	}

	/**
	 * @param array<string, mixed> $settings
	 * @param array<string, mixed> $atts
	 * @return list<array<string, mixed>>
	 */
	private function resolveProvidersForRender( array $settings, array $atts ): array {
		$only = null;
		if ( isset( $atts['providers'] ) && is_string( $atts['providers'] ) && trim( $atts['providers'] ) !== '' ) {
			$only = [];
			foreach ( explode( ',', $atts['providers'] ) as $slug ) {
				$slug = sanitize_key( trim( $slug ) );
				if ( $slug !== '' ) {
					$only[] = $slug;
				}
			}
		}
		return ProviderRegistry::enabledList( $settings, $only );
	}

	/**
	 * @return array<string, string>
	 */
	private function shortcodeDefaults(): array {
		return [
			'title'        => '',
			'providers'    => '',
			'alignment'    => '',
			'size'         => '',
			'show_labels'  => '',
			'open_new_tab' => '',
		];
	}

	/**
	 * @param array<string, mixed> $settings
	 * @param array<string, mixed> $atts
	 * @return array<string, mixed>
	 */
	private function mergeShortcodeOverrides( array $settings, array $atts ): array {
		if ( isset( $atts['title'] ) && is_string( $atts['title'] ) && trim( $atts['title'] ) !== '' ) {
			$settings['heading_text'] = sanitize_text_field( $atts['title'] );
			$settings['show_heading'] = true;
		}

		$alignment = sanitize_key( (string) ( $atts['alignment'] ?? '' ) );
		if ( in_array( $alignment, [ 'left', 'center', 'right' ], true ) ) {
			$settings['alignment'] = $alignment;
		}

		$size = sanitize_key( (string) ( $atts['size'] ?? '' ) );
		if ( in_array( $size, [ 'small', 'medium', 'large' ], true ) ) {
			$settings['button_size'] = $size;
		}

		if ( isset( $atts['show_labels'] ) && $atts['show_labels'] !== '' ) {
			$v = strtolower( trim( (string) $atts['show_labels'] ) );
			if ( in_array( $v, [ '1', 'yes', 'true', 'on' ], true ) ) {
				$settings['button_style'] = 'icon_label';
			} elseif ( in_array( $v, [ '0', 'no', 'false', 'off' ], true ) ) {
				$settings['button_style'] = 'icon';
			}
		}

		if ( isset( $atts['open_new_tab'] ) && $atts['open_new_tab'] !== '' ) {
			$v = strtolower( trim( (string) $atts['open_new_tab'] ) );
			$settings['open_new_tab'] = ! in_array( $v, [ '0', 'no', 'false', 'off' ], true );
		}

		return $settings;
	}

	public function enqueueFrontendAssets(): void {
		if ( self::$assets_enqueued ) {
			return;
		}

		$base = defined( 'WP_POWERSUITE_PLUGIN_URL' ) ? WP_POWERSUITE_PLUGIN_URL : '';
		$css  = __DIR__ . '/assets/ai-summarize.css';
		$js   = __DIR__ . '/assets/ai-summarize.js';

		if ( is_readable( $css ) ) {
			wp_enqueue_style(
				'wpps-ai-summarize',
				$base . 'modules/ai-summarize/assets/ai-summarize.css',
				[],
				$this->assetVersion( 'ai-summarize.css' )
			);
		}

		if ( is_readable( $js ) ) {
			wp_enqueue_script(
				'wpps-ai-summarize',
				$base . 'modules/ai-summarize/assets/ai-summarize.js',
				[],
				$this->assetVersion( 'ai-summarize.js' ),
				true
			);
			wp_localize_script(
				'wpps-ai-summarize',
				'wppsAiSummarize',
				[
					'copied'       => __( 'Prompt copied. Paste it into the AI chat if it did not prefill.', 'wp-powersuite' ),
					'copyFailed'   => __( 'Could not copy the prompt automatically.', 'wp-powersuite' ),
					'popupBlocked' => __( 'Pop-up blocked. Allow pop-ups for this site, or open the link manually.', 'wp-powersuite' ),
				]
			);
		}

		self::$assets_enqueued = true;
	}

	private function assetVersion( string $filename ): string {
		$path = __DIR__ . '/assets/' . $filename;
		return is_readable( $path ) ? (string) filemtime( $path ) : self::MODULE_VERSION;
	}

	/**
	 * @return array<string, mixed>
	 */
	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}
		$raw = $this->moduleSettings()->all( [] );
		$this->settings_cache = self::normalizeSettings( is_array( $raw ) ? $raw : [] );
		return $this->settings_cache;
	}

	/**
	 * @return array<string, mixed>
	 */
	public function getDefaultSettings(): array {
		return self::defaultSettings();
	}

	/**
	 * @param array<string, mixed> $raw
	 * @return array<string, mixed>
	 */
	public static function normalizeSettings( array $raw ): array {
		$defaults = self::defaultSettings();
		$merged   = array_merge( $defaults, $raw );

		$merged['show_heading']               = self::toBool( $merged['show_heading'] ?? true );
		$merged['open_new_tab']               = self::toBool( $merged['open_new_tab'] ?? true );
		$merged['copy_prompt_before_opening'] = self::toBool( $merged['copy_prompt_before_opening'] ?? false );
		$merged['remove_query_params']        = self::toBool( $merged['remove_query_params'] ?? true );
		$merged['disable_logged_out']         = self::toBool( $merged['disable_logged_out'] ?? false );
		$merged['disable_logged_in']          = self::toBool( $merged['disable_logged_in'] ?? false );

		$ht = sanitize_text_field( (string) ( $merged['heading_text'] ?? '' ) );
		$merged['heading_text'] = function_exists( 'mb_substr' ) ? mb_substr( $ht, 0, 120 ) : substr( $ht, 0, 120 );

		foreach (
			[
				'alignment'    => [ 'left', 'center', 'right' ],
				'button_style' => [ 'icon', 'icon_label', 'label' ],
				'button_shape' => [ 'rounded', 'square', 'circle' ],
				'button_size'  => [ 'small', 'medium', 'large' ],
			] as $key => $allowed
		) {
			$val            = sanitize_key( (string) ( $merged[ $key ] ?? $defaults[ $key ] ) );
			$merged[ $key ] = in_array( $val, $allowed, true ) ? $val : $defaults[ $key ];
		}

		$registry_keys         = array_keys( ProviderRegistry::defaults() );
		$providers_in          = isset( $merged['providers'] ) && is_array( $merged['providers'] ) ? $merged['providers'] : [];
		$providers_were_posted = array_key_exists( 'providers', $raw );
		$providers_out         = [];
		foreach ( $registry_keys as $slug ) {
			$row = isset( $providers_in[ $slug ] ) && is_array( $providers_in[ $slug ] ) ? $providers_in[ $slug ] : [];
			if ( $providers_were_posted ) {
				$enabled = array_key_exists( 'enabled', $row ) && self::toBool( $row['enabled'] );
			} else {
				$enabled = array_key_exists( 'enabled', $row )
					? self::toBool( $row['enabled'] )
					: ! empty( ( ProviderRegistry::defaults()[ $slug ]['enabled'] ?? true ) );
			}
			$providers_out[ $slug ] = [
				'enabled' => $enabled,
			];
		}
		$merged['providers'] = $providers_out;

		if ( isset( $merged['provider_order'] ) && is_string( $merged['provider_order'] ) ) {
			$decoded_order = json_decode( wp_unslash( $merged['provider_order'] ), true );
			$merged['provider_order'] = is_array( $decoded_order ) ? $decoded_order : [];
		}

		$order = [];
		if ( isset( $merged['provider_order'] ) && is_array( $merged['provider_order'] ) ) {
			foreach ( $merged['provider_order'] as $slug ) {
				$slug = sanitize_key( (string) $slug );
				if ( $slug !== '' && in_array( $slug, $registry_keys, true ) ) {
					$order[] = $slug;
				}
			}
		}
		foreach ( $registry_keys as $slug ) {
			if ( ! in_array( $slug, $order, true ) ) {
				$order[] = $slug;
			}
		}
		$merged['provider_order'] = $order;

		$clean = [];
		foreach ( array_keys( $defaults ) as $key ) {
			$clean[ $key ] = $merged[ $key ] ?? $defaults[ $key ];
		}

		return $clean;
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function defaultSettings(): array {
		$providers = [];
		$order     = [];
		foreach ( ProviderRegistry::defaults() as $slug => $def ) {
			$providers[ $slug ] = [
				'enabled' => ! empty( $def['enabled'] ),
			];
			$order[] = $slug;
		}

		return [
			'show_heading'               => true,
			'heading_text'               => 'Summarize with AI:',
			'open_new_tab'               => true,
			'copy_prompt_before_opening' => false,
			'remove_query_params'        => true,
			'disable_logged_out'         => false,
			'disable_logged_in'          => false,
			'providers'                  => $providers,
			'provider_order'             => $order,
			'alignment'                  => 'left',
			'button_style'               => 'icon_label',
			'button_shape'               => 'rounded',
			'button_size'                => 'medium',
		];
	}

	/**
	 * @param mixed $value
	 */
	private static function toBool( $value ): bool {
		if ( is_bool( $value ) ) {
			return $value;
		}
		if ( is_string( $value ) ) {
			return ! in_array( strtolower( $value ), [ '', '0', 'false', 'no', 'off' ], true );
		}
		return ! empty( $value );
	}
}
