<?php
/**
 * Secret retrieval, wp-config constant overrides, and UI masking helpers.
 *
 * @package Newsroom_AI_Assistant
 */

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

if ( ! function_exists( 'newsai_secrets_registry' ) ) {
	/**
	 * @return array<string, array{option:string, constant:string}>
	 */
	function newsai_secrets_registry() {
		$registry = [
			'openai_api_key'          => [
				'option'   => 'newsai_ai_openai_api_key',
				'constant' => 'NEWSAI_OPENAI_API_KEY',
			],
			'gemini_api_key'          => [
				'option'   => 'newsai_ai_gemini_api_key',
				'constant' => 'NEWSAI_GEMINI_API_KEY',
			],
			'groq_api_key'            => [
				'option'   => 'newsai_ai_groq_api_key',
				'constant' => 'NEWSAI_GROQ_API_KEY',
			],
			'indexing_service_json'   => [
				'option'   => 'newsai_indexing_service_json',
				'constant' => 'NEWSAI_INDEXING_SERVICE_JSON',
			],
			'pro_license_key'         => [
				'option'   => 'newsai_pro_license_key',
				'constant' => 'NEWSAI_PRO_LICENSE_KEY',
			],
		];

		return apply_filters( 'newsai_secrets_registry', $registry );
	}
}

if ( ! function_exists( 'newsai_secret_is_locked' ) ) {
	/**
	 * True when the secret is supplied via wp-config.php (not editable in admin).
	 *
	 * @param string $id Registry key.
	 */
	function newsai_secret_is_locked( $id ) {
		$registry = newsai_secrets_registry();
		if ( ! isset( $registry[ $id ] ) ) {
			return false;
		}
		$constant = $registry[ $id ]['constant'];
		if ( $constant === '' || ! defined( $constant ) ) {
			return false;
		}
		$value = constant( $constant );
		if ( is_string( $value ) ) {
			return trim( $value ) !== '';
		}
		return ! empty( $value );
	}
}

if ( ! function_exists( 'newsai_secret_get' ) ) {
	/**
	 * Resolve secret: wp-config constant wins, else non-autoloaded option.
	 *
	 * @param string $id Registry key.
	 */
	function newsai_secret_get( $id ) {
		$registry = newsai_secrets_registry();
		if ( ! isset( $registry[ $id ] ) ) {
			return '';
		}
		$constant = $registry[ $id ]['constant'];
		if ( $constant !== '' && defined( $constant ) ) {
			$from_const = constant( $constant );
			if ( is_string( $from_const ) && trim( $from_const ) !== '' ) {
				return trim( $from_const );
			}
		}
		return trim( (string) get_option( $registry[ $id ]['option'], '' ) );
	}
}

if ( ! function_exists( 'newsai_secret_has_stored' ) ) {
	/**
	 * @param string $id Registry key.
	 */
	function newsai_secret_has_stored( $id ) {
		return newsai_secret_get( $id ) !== '';
	}
}

if ( ! function_exists( 'newsai_secret_mask' ) ) {
	/**
	 * Mask a secret for placeholders / status text (never output full value).
	 *
	 * @param string $value Raw secret.
	 */
	function newsai_secret_mask( $value ) {
		$value = (string) $value;
		$len   = strlen( $value );
		if ( $len === 0 ) {
			return '';
		}
		if ( $len <= 8 ) {
			return str_repeat( '•', $len );
		}
		return str_repeat( '•', min( 24, $len - 4 ) ) . substr( $value, -4 );
	}
}

if ( ! function_exists( 'newsai_secret_option_name' ) ) {
	/**
	 * @param string $id Registry key.
	 */
	function newsai_secret_option_name( $id ) {
		$registry = newsai_secrets_registry();
		return isset( $registry[ $id ] ) ? $registry[ $id ]['option'] : '';
	}
}

if ( ! function_exists( 'newsai_secret_update_option' ) ) {
	/**
	 * Persist to wp_options unless locked by constant.
	 *
	 * @param string $id    Registry key.
	 * @param string $value Secret value.
	 */
	function newsai_secret_update_option( $id, $value ) {
		if ( newsai_secret_is_locked( $id ) ) {
			return;
		}
		$option = newsai_secret_option_name( $id );
		if ( $option === '' ) {
			return;
		}
		update_option( $option, $value, false );
	}
}

if ( ! function_exists( 'newsai_secret_delete_option' ) ) {
	/**
	 * @param string $id Registry key.
	 */
	function newsai_secret_delete_option( $id ) {
		if ( newsai_secret_is_locked( $id ) ) {
			return;
		}
		$option = newsai_secret_option_name( $id );
		if ( $option !== '' ) {
			delete_option( $option );
		}
	}
}

if ( ! function_exists( 'newsai_normalize_sa_private_key' ) ) {
	/**
	 * Repair common PEM / newline corruption from paste, WAF, or wp-config defines.
	 *
	 * @param string $private_key Raw private_key from service account JSON.
	 */
	function newsai_normalize_sa_private_key( $private_key ) {
		$private_key = (string) $private_key;
		if ( $private_key === '' ) {
			return '';
		}

		if ( strpos( $private_key, '\\n' ) !== false ) {
			$private_key = str_replace( '\\n', "\n", $private_key );
		}
		if ( strpos( $private_key, '\n' ) !== false && strpos( $private_key, "\n" ) === false ) {
			$private_key = str_replace( '\n', "\n", $private_key );
		}
		$private_key = str_replace( [ "\r\n", "\r" ], "\n", $private_key );
		$private_key = trim( $private_key );

		if ( preg_match( '/-----BEGIN (RSA )?PRIVATE KEY-----\s*(.*?)\s*-----END \1PRIVATE KEY-----/s', $private_key, $matches ) ) {
			$label = ! empty( $matches[1] ) ? 'RSA PRIVATE KEY' : 'PRIVATE KEY';
			$body  = preg_replace( '/\s+/', '', (string) $matches[2] );
			if ( $body !== '' ) {
				$private_key = '-----BEGIN ' . $label . "-----\n" . chunk_split( $body, 64, "\n" ) . '-----END ' . $label . "-----\n";
			}
		}

		return $private_key;
	}
}

if ( ! function_exists( 'newsai_sa_openssl_private_key' ) ) {
	/**
	 * @param string $private_key Raw or normalized PEM.
	 * @return resource|\OpenSSLAsymmetricKey|false
	 */
	function newsai_sa_openssl_private_key( $private_key ) {
		$private_key = newsai_normalize_sa_private_key( $private_key );
		if ( $private_key === '' ) {
			return false;
		}
		return openssl_pkey_get_private( $private_key );
	}
}

if ( ! function_exists( 'newsai_sa_private_key_openssl_error' ) ) {
	/**
	 * Last OpenSSL error line, if any.
	 */
	function newsai_sa_private_key_openssl_error() {
		$last = '';
		while ( $msg = openssl_error_string() ) {
			$last = (string) $msg;
		}
		return $last;
	}
}

if ( ! function_exists( 'newsai_secret_indexing_client_email' ) ) {
	/**
	 * Client email for status UI without exposing private_key JSON.
	 */
	function newsai_secret_indexing_client_email() {
		$stored = (string) get_option( 'newsai_indexing_service_email', '' );
		if ( $stored !== '' ) {
			return sanitize_email( $stored );
		}
		$json = newsai_secret_get( 'indexing_service_json' );
		if ( $json === '' ) {
			return '';
		}
		$decoded = json_decode( $json, true );
		if ( ! is_array( $decoded ) || empty( $decoded['client_email'] ) ) {
			return '';
		}
		return sanitize_email( (string) $decoded['client_email'] );
	}
}

if ( ! function_exists( 'newsai_save_indexing_service_json_from_request' ) ) {
	/**
	 * Persist Google service account JSON from admin POST (dedicated save or main settings form).
	 *
	 * @param array<string, mixed> $source Typically $_POST.
	 * @return true|WP_Error
	 */
	function newsai_save_indexing_service_json_from_request( array $source ) {
		if ( function_exists( 'newsai_secret_is_locked' ) && newsai_secret_is_locked( 'indexing_service_json' ) ) {
			return new WP_Error(
				'indexing_sa_locked',
				__( 'Credentials are supplied via NEWSAI_INDEXING_SERVICE_JSON in wp-config.php and cannot be changed here.', 'newsroom-ai-assistant' )
			);
		}
		if ( function_exists( 'newsai_is_pro' ) && ! newsai_is_pro() ) {
			return new WP_Error(
				'indexing_sa_pro',
				__( 'Pro license required to store the Google service account JSON.', 'newsroom-ai-assistant' )
			);
		}

		$clear = ! empty( $source['indexing_service_json_clear'] ) && (string) $source['indexing_service_json_clear'] === '1';
		if ( $clear ) {
			if ( function_exists( 'newsai_secret_delete_option' ) ) {
				newsai_secret_delete_option( 'indexing_service_json' );
			} else {
				update_option( 'newsai_indexing_service_json', '', false );
			}
			update_option( 'newsai_indexing_service_email', '', false );
			delete_transient( 'newsai_indexing_sa_access_token' );
			delete_transient( 'newsai_gsc_sa_access_token' );
			delete_transient( 'newsai_psi_sa_access_token' );
			return true;
		}

		$raw = isset( $source['indexing_service_json'] ) ? trim( (string) wp_unslash( $source['indexing_service_json'] ) ) : '';
		if ( $raw === '' ) {
			return new WP_Error(
				'indexing_sa_empty',
				__( 'Paste the full service account JSON file from Google Cloud (must start with { and include client_email + private_key).', 'newsroom-ai-assistant' )
			);
		}

		$raw = preg_replace( '/^\xEF\xBB\xBF/', '', $raw );
		$had_json = function_exists( 'newsai_secret_has_stored' ) && newsai_secret_has_stored( 'indexing_service_json' );
		$replace  = ! empty( $source['indexing_service_json_replace'] ) && (string) $source['indexing_service_json_replace'] === '1';
		if ( $had_json && ! $replace ) {
			return new WP_Error(
				'indexing_sa_replace',
				__( 'A service account is already stored. Check “Replace service account JSON” before pasting a new file.', 'newsroom-ai-assistant' )
			);
		}

		$decoded = json_decode( $raw, true );
		if ( ! is_array( $decoded ) ) {
			$json_err = function_exists( 'json_last_error_msg' ) ? json_last_error_msg() : '';
			return new WP_Error(
				'indexing_sa_invalid',
				sprintf(
					/* translators: 1: json_decode error message */
					__( 'Invalid JSON (%1$s). Download the key again from Google Cloud → IAM → Service accounts → Keys, and paste the entire file.', 'newsroom-ai-assistant' ),
					$json_err !== '' ? $json_err : __( 'parse error', 'newsroom-ai-assistant' )
				)
			);
		}
		if ( empty( $decoded['client_email'] ) || empty( $decoded['private_key'] ) ) {
			return new WP_Error(
				'indexing_sa_fields',
				__( 'JSON is missing client_email or private_key. Paste the complete .json key file — not a fragment from the middle of the file.', 'newsroom-ai-assistant' )
			);
		}

		$decoded['private_key'] = newsai_normalize_sa_private_key( (string) $decoded['private_key'] );
		if ( ! newsai_sa_openssl_private_key( $decoded['private_key'] ) ) {
			$ossl = newsai_sa_private_key_openssl_error();
			return new WP_Error(
				'indexing_sa_pkey',
				$ossl !== ''
					? sprintf(
						/* translators: 1: OpenSSL error message */
						__( 'Private key is not valid PEM (%1$s). Download a fresh .json key from Google Cloud and paste the entire file.', 'newsroom-ai-assistant' ),
						$ossl
					)
					: __( 'Private key is not valid PEM. Download a fresh .json key from Google Cloud and paste the entire file.', 'newsroom-ai-assistant' )
			);
		}

		$encoded = wp_json_encode( $decoded, JSON_UNESCAPED_SLASHES );
		if ( ! is_string( $encoded ) || $encoded === '' ) {
			return new WP_Error( 'indexing_sa_encode', __( 'Could not encode service account JSON.', 'newsroom-ai-assistant' ) );
		}
		if ( function_exists( 'newsai_secret_update_option' ) ) {
			newsai_secret_update_option( 'indexing_service_json', $encoded );
		} else {
			update_option( 'newsai_indexing_service_json', $encoded, false );
		}
		update_option( 'newsai_indexing_service_email', sanitize_email( (string) $decoded['client_email'] ), false );
		delete_transient( 'newsai_indexing_sa_access_token' );
		delete_transient( 'newsai_gsc_sa_access_token' );
		delete_transient( 'newsai_psi_sa_access_token' );

		return true;
	}
}
