<?php
/**
 * Site-wide AI provider connections shared across PowerSuite modules.
 *
 * @package WP_PowerSuite\AI
 */

namespace WP_PowerSuite\AI;

use WP_PowerSuite\Core\OptionStoreModuleSettingsRepository;

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

/**
 * Centralized AI API key storage and resolution.
 */
final class ConnectionSettings {

	public const MODULE_ID = 'ai-connections';

	public const OPENAI_KEY_CONSTANT     = 'WPPS_AI_OPENAI_KEY';
	public const GEMINI_KEY_CONSTANT     = 'WPPS_AI_GEMINI_KEY';
	public const CLAUDE_KEY_CONSTANT     = 'WPPS_AI_CLAUDE_KEY';
	public const OPENROUTER_KEY_CONSTANT = 'WPPS_AI_OPENROUTER_KEY';
	public const DEEPSEEK_KEY_CONSTANT   = 'WPPS_AI_DEEPSEEK_KEY';
	public const GROK_KEY_CONSTANT       = 'WPPS_AI_GROK_KEY';

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

	/**
	 * @return array<string, mixed>
	 */
	public static function defaults(): array {
		$defaults = [
			'provider'             => 'openai',
			'use_network_api_keys' => true,
		];

		foreach ( ProviderRegistry::keyFields() as $field ) {
			$defaults[ $field ] = '';
		}

		return $defaults;
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function get(): array {
		if ( self::$cache !== null ) {
			return self::$cache;
		}

		$saved  = OptionStoreModuleSettingsRepository::forModule( self::MODULE_ID )->all( [] );
		$merged = is_array( $saved ) ? array_merge( self::defaults(), $saved ) : self::defaults();
		$merged['provider'] = self::sanitizeProvider( (string) ( $merged['provider'] ?? 'openai' ) );

		self::$cache = $merged;

		return self::$cache;
	}

	public static function flushCache(): void {
		self::$cache = null;
	}

	/**
	 * Safe snapshot for admin clients (no secrets).
	 *
	 * @return array<string, mixed>
	 */
	public static function getPublic(): array {
		$settings = self::get();
		$provider = (string) ( $settings['provider'] ?? 'openai' );
		$status   = [];

		foreach ( ProviderRegistry::ids() as $provider_id ) {
			$status[ $provider_id ] = self::hasApiKey( $provider_id );
		}

		return [
			'provider'             => $provider,
			'has_api_key'          => self::hasApiKey( $provider ),
			'provider_status'      => $status,
			'use_network_api_keys' => ! empty( $settings['use_network_api_keys'] ),
			'providers'            => self::providerLabels(),
		];
	}

	/** @return array<string, string> */
	public static function providerLabels(): array {
		return ProviderRegistry::labels();
	}

	/**
	 * @return array<string, array{
	 *     field: string,
	 *     input_id: string,
	 *     network_input_id: string,
	 *     config_constant: string,
	 *     placeholder: string,
	 *     description?: string
	 * }>
	 */
	public static function providerFieldMeta(): array {
		return ProviderRegistry::fieldMeta();
	}

	public static function sanitizeProvider( string $provider ): string {
		return ProviderRegistry::sanitize( $provider );
	}

	public static function isApiKeyFromConfig( ?string $provider = null ): bool {
		$constant = self::configConstantForProvider( $provider );
		return $constant !== '' && defined( $constant ) && trim( (string) constant( $constant ) ) !== '';
	}

	public static function isApiKeyFromNetwork( ?string $provider = null ): bool {
		if ( ! NetworkSettings::isMultisite() || empty( self::get()['use_network_api_keys'] ) ) {
			return false;
		}

		$provider = self::sanitizeProvider( $provider ?? self::get()['provider'] ?? 'openai' );
		$field    = self::keyFieldForProvider( $provider );

		return NetworkSettings::isNetworkKeyConfigured( $field );
	}

	public static function hasApiKey( ?string $provider = null ): bool {
		return self::getApiKey( null, $provider ) !== '';
	}

	/**
	 * Provider IDs that have a resolvable API key.
	 *
	 * @return list<string>
	 */
	public static function getConfiguredProviders(): array {
		$configured = [];
		foreach ( ProviderRegistry::ids() as $provider_id ) {
			if ( self::hasApiKey( $provider_id ) ) {
				$configured[] = $provider_id;
			}
		}

		return $configured;
	}

	/**
	 * Configured providers that PowerSuite modules can call for chat completions.
	 *
	 * @return list<string>
	 */
	public static function getModuleConfiguredProviders(): array {
		$configured = [];
		foreach ( self::getConfiguredProviders() as $provider_id ) {
			if ( ProviderRegistry::supportsModules( $provider_id ) ) {
				$configured[] = $provider_id;
			}
		}

		return $configured;
	}

	/**
	 * Resolve the effective API key for a provider.
	 */
	public static function getApiKey( ?array $settings = null, ?string $provider = null ): string {
		$settings = $settings ?? self::get();
		$provider = self::sanitizeProvider( $provider ?? (string) ( $settings['provider'] ?? 'openai' ) );

		$constant = self::configConstantForProvider( $provider );
		if ( $constant !== '' && defined( $constant ) ) {
			$value = trim( (string) constant( $constant ) );
			if ( $value !== '' ) {
				return (string) apply_filters( 'wpps_ai_connection_api_key', $value, $provider, 'config' );
			}
		}

		$field      = self::keyFieldForProvider( $provider );
		$site_value = trim( (string) ( $settings[ $field ] ?? '' ) );
		$key        = NetworkSettings::resolve( $site_value, $field );

		/**
		 * Filter the resolved site-wide AI API key.
		 *
		 * @param string $key      Resolved key (may be empty).
		 * @param string $provider Provider ID.
		 * @param string $source   `site`, `network`, or `config`.
		 */
		return (string) apply_filters(
			'wpps_ai_connection_api_key',
			$key,
			$provider,
			$site_value !== '' ? 'site' : ( NetworkSettings::isNetworkKeyConfigured( $field ) ? 'network' : 'site' )
		);
	}

	private static function keyFieldForProvider( string $provider ): string {
		return ProviderRegistry::keyField( $provider );
	}

	private static function configConstantForProvider( ?string $provider ): string {
		return ProviderRegistry::configConstant( $provider ?? self::get()['provider'] ?? 'openai' );
	}

	/**
	 * @param array<string, mixed> $settings Raw settings.
	 * @param array<string, mixed> $existing Existing stored settings.
	 * @return array<string, mixed>
	 */
	public static function normalize( array $settings, array $existing = [] ): array {
		$normalized = array_merge( self::defaults(), $existing, $settings );
		$normalized['provider'] = self::sanitizeProvider( (string) ( $settings['provider'] ?? $normalized['provider'] ?? 'openai' ) );

		foreach ( ProviderRegistry::definitions() as $definition ) {
			$field    = (string) ( $definition['key_field'] ?? '' );
			$constant = (string) ( $definition['config_constant'] ?? '' );
			if ( $field === '' ) {
				continue;
			}

			if ( $constant !== '' && defined( $constant ) && trim( (string) constant( $constant ) ) !== '' ) {
				unset( $normalized[ $field ] );
				continue;
			}

			if ( isset( $settings[ $field ] ) && trim( (string) $settings[ $field ] ) !== '' ) {
				$normalized[ $field ] = sanitize_text_field( (string) $settings[ $field ] );
			} elseif ( ! empty( $existing[ $field ] ) ) {
				$normalized[ $field ] = (string) $existing[ $field ];
			} else {
				$normalized[ $field ] = '';
			}
		}

		$normalized['use_network_api_keys'] = ! empty( $settings['use_network_api_keys'] );

		if ( NetworkSettings::canManageNetworkKeys() && is_multisite() ) {
			foreach ( NetworkSettings::keyFields() as $field ) {
				if ( isset( $settings[ 'network_' . $field ] ) && trim( (string) $settings[ 'network_' . $field ] ) !== '' ) {
					NetworkSettings::update( $field, sanitize_text_field( (string) $settings[ 'network_' . $field ] ) );
				}
			}
		}

		return $normalized;
	}

	/**
	 * @param array<string, mixed> $raw Raw POST payload.
	 */
	public static function persist( array $raw ): bool {
		$repo     = OptionStoreModuleSettingsRepository::forModule( self::MODULE_ID );
		$existing = $repo->all( [] );
		if ( ! is_array( $existing ) ) {
			$existing = [];
		}

		$normalized = self::normalize( $raw, $existing );
		$saved      = $repo->save( $normalized );
		if ( $saved ) {
			self::flushCache();
		}

		return $saved;
	}
}
