<?php
/**
 * Extensible AI chat provider registry for AI Summarize.
 *
 * @package AISummarize
 */

namespace AISummarize;

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

/**
 * Provider definitions and icon helpers.
 *
 * Prefill URL formats are based on currently documented public query parameters
 * and may change when third-party services update their apps.
 */
final class ProviderRegistry {

	/**
	 * Built-in providers (defaults before filters / settings overrides).
	 *
	 * @return array<string, array<string, mixed>>
	 */
	public static function defaults(): array {
		$defaults = [
			'chatgpt'    => [
				'name'              => 'ChatGPT',
				'url'               => 'https://chatgpt.com/',
				'prefill_supported' => true,
				'prompt_parameter'  => 'q',
				'icon'              => 'chatgpt',
				'brand_color'       => '#10a37f',
				'enabled'           => true,
				'order'             => 10,
			],
			'gemini'     => [
				'name'              => 'Gemini',
				// Google AI Mode (Search) supports ?q=; gemini.google.com/app does not.
				'url'               => 'https://www.google.com/search?udm=50',
				'prefill_supported' => true,
				'prompt_parameter'  => 'q',
				'icon'              => 'gemini',
				'brand_color'       => '#8e75b2',
				'enabled'           => true,
				'order'             => 20,
			],
			'claude'     => [
				'name'              => 'Claude',
				'url'               => 'https://claude.ai/new',
				'prefill_supported' => true,
				'prompt_parameter'  => 'q',
				'icon'              => 'claude',
				'brand_color'       => '#d97757',
				'enabled'           => true,
				'order'             => 30,
			],
			'perplexity' => [
				'name'              => 'Perplexity',
				'url'               => 'https://www.perplexity.ai/search',
				'prefill_supported' => true,
				'prompt_parameter'  => 'q',
				'icon'              => 'perplexity',
				'brand_color'       => '#20808d',
				'enabled'           => true,
				'order'             => 40,
			],
			'grok'       => [
				'name'              => 'Grok',
				'url'               => 'https://grok.com/',
				'prefill_supported' => true,
				'prompt_parameter'  => 'q',
				'icon'              => 'grok',
				'brand_color'       => '#000000',
				'enabled'           => true,
				'order'             => 50,
			],
		];

		/**
		 * Filter the built-in AI Summarize provider registry.
		 *
		 * @param array<string, array<string, mixed>> $defaults Provider definitions keyed by slug.
		 */
		return apply_filters( 'wpps_ai_summarize_providers', $defaults );
	}

	/**
	 * Merge registry defaults with saved provider settings.
	 *
	 * @param array<string, mixed> $settings Normalized module settings.
	 * @return array<string, array<string, mixed>>
	 */
	public static function resolve( array $settings ): array {
		$registry = self::defaults();
		$saved    = isset( $settings['providers'] ) && is_array( $settings['providers'] ) ? $settings['providers'] : [];
		$order    = isset( $settings['provider_order'] ) && is_array( $settings['provider_order'] ) ? $settings['provider_order'] : [];

		foreach ( $registry as $slug => &$provider ) {
			$override = isset( $saved[ $slug ] ) && is_array( $saved[ $slug ] ) ? $saved[ $slug ] : [];
			if ( array_key_exists( 'enabled', $override ) ) {
				$provider['enabled'] = ! empty( $override['enabled'] );
			}
			$provider['slug'] = $slug;
		}
		unset( $provider );

		if ( $order !== [] ) {
			$sorted = [];
			$i      = 0;
			foreach ( $order as $slug ) {
				$slug = sanitize_key( (string) $slug );
				if ( $slug !== '' && isset( $registry[ $slug ] ) ) {
					$registry[ $slug ]['order'] = $i * 10;
					$sorted[ $slug ]            = $registry[ $slug ];
					++$i;
				}
			}
			foreach ( $registry as $slug => $provider ) {
				if ( ! isset( $sorted[ $slug ] ) ) {
					$sorted[ $slug ] = $provider;
				}
			}
			$registry = $sorted;
		} else {
			uasort(
				$registry,
				static function ( array $a, array $b ): int {
					return ( (int) ( $a['order'] ?? 0 ) ) <=> ( (int) ( $b['order'] ?? 0 ) );
				}
			);
		}

		return $registry;
	}

	/**
	 * Enabled providers in display order.
	 *
	 * @param array<string, mixed> $settings
	 * @param list<string>|null    $only_slugs Optional shortcode filter.
	 * @return list<array<string, mixed>>
	 */
	public static function enabledList( array $settings, ?array $only_slugs = null ): array {
		$resolved = self::resolve( $settings );
		$out      = [];
		foreach ( $resolved as $slug => $provider ) {
			if ( empty( $provider['enabled'] ) ) {
				continue;
			}
			if ( is_array( $only_slugs ) && $only_slugs !== [] && ! in_array( $slug, $only_slugs, true ) ) {
				continue;
			}
			$out[] = $provider;
		}
		return $out;
	}

	/**
	 * Build outbound provider URL (prefill when supported).
	 *
	 * @param array<string, mixed> $provider
	 * @param string               $prompt
	 */
	public static function buildLaunchUrl( array $provider, string $prompt ): string {
		$base = self::sanitizeHttpsUrl( (string) ( $provider['url'] ?? '' ) );
		if ( $base === '' ) {
			return '';
		}

		$url = $base;
		if ( ! empty( $provider['prefill_supported'] ) && $prompt !== '' ) {
			$param = sanitize_key( (string) ( $provider['prompt_parameter'] ?? 'q' ) );
			if ( $param === '' ) {
				$param = 'q';
			}
			// esc_url / esc_url_raw strip newlines (%0A) from URLs, which glues
			// "content:\n\nhttps://…" into "content:https://…". Flatten for query use.
			$query_prompt = self::normalizePromptForUrl( $prompt );
			$url          = add_query_arg( $param, $query_prompt, $base );
			$url          = self::sanitizeHttpsUrl( is_string( $url ) ? $url : $base );
			if ( $url === '' ) {
				$url = $base;
			}
		}

		/**
		 * Filter the final provider launch URL.
		 *
		 * @param string               $url      Launch URL.
		 * @param array<string, mixed> $provider Provider definition.
		 * @param string               $prompt   Generated prompt text.
		 */
		$filtered = apply_filters( 'wpps_ai_summarize_provider_url', $url, $provider, $prompt );

		return is_string( $filtered ) ? self::sanitizeHttpsUrl( $filtered ) : $url;
	}

	/**
	 * Collapse whitespace so prompt survives WordPress URL escaping.
	 */
	public static function normalizePromptForUrl( string $prompt ): string {
		$prompt = str_replace( [ "\r\n", "\r", "\n", "\t" ], ' ', $prompt );
		$prompt = preg_replace( '/\s+/u', ' ', $prompt ) ?? $prompt;
		return trim( $prompt );
	}

	/**
	 * @param string $url
	 */
	public static function sanitizeHttpsUrl( string $url ): string {
		$url = trim( $url );
		if ( $url === '' ) {
			return '';
		}

		$url = esc_url_raw( $url, [ 'https' ] );
		if ( ! is_string( $url ) || $url === '' ) {
			return '';
		}

		$parts = wp_parse_url( $url );
		if ( ! is_array( $parts ) || empty( $parts['scheme'] ) || strtolower( (string) $parts['scheme'] ) !== 'https' ) {
			return '';
		}
		if ( empty( $parts['host'] ) ) {
			return '';
		}

		return $url;
	}

	/**
	 * @param string $color
	 * @param string $fallback
	 */
	public static function sanitizeHexColor( string $color, string $fallback = '#2271b1' ): string {
		$color = trim( $color );
		if ( preg_match( '/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6})$/', $color ) ) {
			return strtolower( $color );
		}
		$sanitized = sanitize_hex_color( $color );
		return is_string( $sanitized ) && $sanitized !== '' ? strtolower( $sanitized ) : $fallback;
	}

	/**
	 * Official brand mark SVGs (decorative only).
	 * ChatGPT/Claude/Perplexity/Gemini: Simple Icons (CC0).
	 * Grok: Simple Icons "x" (X / xAI brand mark).
	 */
	public static function getIconSvg( string $icon ): string {
		$icon = sanitize_key( $icon );
		$wrap = static function ( string $path ): string {
			return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true" focusable="false"><path d="' . $path . '"/></svg>';
		};

		$map = [
			// OpenAI / ChatGPT — Simple Icons "openai".
			'chatgpt'    => $wrap( 'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z' ),
			// Google Gemini — Simple Icons "googlegemini".
			'gemini'     => $wrap( 'M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81' ),
			// Anthropic / Claude — Simple Icons "anthropic".
			'claude'     => $wrap( 'M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z' ),
			// Perplexity — Simple Icons "perplexity".
			'perplexity' => $wrap( 'M22.3977 7.0896h-2.3106V.0676l-7.5094 6.3542V.1577h-1.1554v6.1966L4.4904 0v7.0896H1.6023v10.3976h2.8882V24l6.932-6.3591v6.2005h1.1554v-6.0469l6.9318 6.1807v-6.4879h2.8882V7.0896zm-3.4657-4.531v4.531h-5.355l5.355-4.531zm-13.2862.0676 4.8691 4.4634H5.6458V2.6262zM2.7576 16.332V8.245h7.8476l-6.1149 6.1147v1.9723H2.7576zm2.8882 5.0404v-3.8852h.0001v-2.6488l5.7763-5.7764v7.0111l-5.7764 5.2993zm12.7086.0248-5.7766-5.1509V9.0618l5.7766 5.7766v6.5588zm2.8882-5.0652h-1.733v-1.9723L13.3948 8.245h7.8478v8.087z' ),
			// Grok / xAI — Simple Icons "x".
			'grok'       => $wrap( 'M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z' ),
			'sparkle'    => $wrap( 'M12 2l1.6 6.4L20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6L12 2zm7 10 1 3 3 1-3 1-1 3-1-3-3-1 3-1 1-3z' ),
		];

		return $map[ $icon ] ?? $map['sparkle'];
	}
}
