<?php
/**
 * Admin asset enqueueing.
 *
 * @package StoreSheet
 */

declare( strict_types=1 );

namespace StoreSheet\Settings;

defined( 'ABSPATH' ) || exit;

class Assets {

	public function init(): void {
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_freemius_notices' ) );
	}

	public function enqueue( string $hook ): void {
		if ( ! str_contains( $hook, 'storesheet' ) ) {
			return;
		}

		$build = STORESHEET_DIR . 'build/';
		$url   = STORESHEET_URL . 'build/';

		wp_enqueue_style(
			'storesheet-admin',
			$url . 'style-admin.css',
			array(),
			$this->asset_version( $build . 'style-admin.css' )
		);
		wp_style_add_data( 'storesheet-admin', 'rtl', 'replace' );

		// Branded Freemius pages (server-rendered .fs-* markup, not the SPA).
		$fs_css = STORESHEET_URL . 'assets/css/';
		$fs_dir = STORESHEET_DIR . 'assets/css/';

		// Opt-in / connect screen — no-op when #fs_connect is absent.
		wp_enqueue_style( 'storesheet-fs-connect', $fs_css . 'freemius-connect.css', array(), $this->asset_version( $fs_dir . 'freemius-connect.css' ) );

		// Freemius renders the checkout overlay on the pricing page/hook, so this covers both.
		if ( str_contains( $hook, 'storesheet-pricing' ) ) {
			wp_enqueue_style( 'storesheet-fs-pricing', $fs_css . 'freemius-pricing.css', array(), $this->asset_version( $fs_dir . 'freemius-pricing.css' ) );
		}

		if ( str_contains( $hook, 'storesheet-account' ) ) {
			wp_enqueue_style( 'storesheet-fs-account', $fs_css . 'freemius-account.css', array(), $this->asset_version( $fs_dir . 'freemius-account.css' ) );
		}

		wp_enqueue_script(
			'storesheet-admin',
			$url . 'admin.js',
			$this->get_script_deps( $build . 'admin.asset.php' ),
			$this->asset_version( $build . 'admin.js' ),
			true
		);

		$settings = get_option( 'storesheet_settings', array() );
		$oauth    = storesheet()->oauth_client();

		$is_pro         = storesheet()->is_pro();
		$license_active = storesheet()->is_license_active();

		// When a license is active but the Pro build isn't installed, the SDK's
		// own account page is where the user downloads the premium version, so
		// use it as the default for accountUrl (the Pro add-on isn't there to
		// supply one via the filter in that gap state).
		$fs              = function_exists( 'storesheet_fs' ) ? storesheet_fs() : null;
		$account_default = ( $fs instanceof \Freemius ) ? $fs->get_account_url() : '';

		wp_localize_script(
			'storesheet-admin',
			'storesheetAdmin',
			apply_filters(
				'storesheet_admin_localize_data',
				array(
					'nonce'           => wp_create_nonce( 'wp_rest' ),
					'restUrl'         => rest_url( 'storesheet/v1' ),
					'settings'        => $this->safe_settings( $settings ),
					'plan'            => $is_pro ? 'pro' : 'free',
					'isPro'           => $is_pro,
					// Valid license regardless of whether the Pro build is
					// installed. `licenseActive && ! isPro` is the gap state that
					// drives the "install StoreSheet Pro" prompt.
					'licenseActive'   => $license_active,
					// Freemius-driven URLs. The Pro add-on overrides these (via
					// this same filter) with real Freemius checkout/account URLs
					// once it is installed. Falls back to the marketing page.
					'upgradeUrl'      => apply_filters( 'storesheet_upgrade_url', 'https://alaminahamed.com/projects/storesheet/#pricing' ),
					'accountUrl'      => apply_filters( 'storesheet_account_url', $account_default ),
					'oauthStatus'     => $oauth->get_status(),
					'pluginUrl'       => STORESHEET_URL,
					'version'         => STORESHEET_VERSION,
					'adminUrl'        => admin_url(),
					'detectedSources' => $this->detected_sources(),
				)
			)
		);
	}

	/**
	 * Freemius admin notices can render on any admin page, so their styles
	 * load globally (independent of the plugin-page guard in enqueue()).
	 */
	public function enqueue_freemius_notices(): void {
		wp_enqueue_style(
			'storesheet-fs-notices',
			STORESHEET_URL . 'assets/css/freemius-notices.css',
			array(),
			$this->asset_version( STORESHEET_DIR . 'assets/css/freemius-notices.css' )
		);
	}

	public function safe_settings( array $settings ): array {
		$key_map = array(
			'oauth_client_secret' => 'oauth_client_secret_exists',
			'oauth_refresh_token' => 'oauth_token_exists',
			'webhook_secret'      => 'webhook_secret_exists',
		);

		/**
		 * Filter the list of settings keys treated as secrets, so add-ons can
		 * register their own masked secret fields (e.g. the Pro build's
		 * `openai_api_key`) without forking this method.
		 *
		 * @since 1.2.0
		 * @param string[] $keys Secret setting keys.
		 */
		$keys = (array) apply_filters(
			'storesheet_secret_keys',
			array_keys( $key_map )
		);

		foreach ( $keys as $key ) {
			$exists_key              = $key_map[ $key ] ?? "{$key}_exists";
			$settings[ $exists_key ] = ! empty( $settings[ $key ] );
			unset( $settings[ $key ] );
		}

		/*
		 * The client id is masked rather than stripped.
		 *
		 * It is not a secret — Google puts it in the consent URL, where any
		 * user can read it — so removing it outright would be security theatre
		 * and would cost the one thing it is useful for: telling at a glance
		 * *which* OAuth client a site is configured against.
		 *
		 * Masking still earns its place. The Connect tab is a screen people
		 * screenshot for support threads and documentation, and a full client
		 * id identifies the Cloud project behind it. Showing enough to
		 * recognise the value and not enough to reproduce it costs nothing.
		 */
		if ( ! empty( $settings['oauth_client_id'] ) ) {
			$settings['oauth_client_id_masked'] = self::mask_client_id( (string) $settings['oauth_client_id'] );
			$settings['oauth_client_id_exists'] = true;
			unset( $settings['oauth_client_id'] );
		} else {
			$settings['oauth_client_id_masked'] = '';
			$settings['oauth_client_id_exists'] = false;
		}

		return $settings;
	}

	/**
	 * Render a client id for display without showing all of it.
	 *
	 * Google client ids look like
	 * `863757670314-uqeiu8rnndq2a4cvs3pg9qanm0j33di0.apps.googleusercontent.com`.
	 * The `.apps.googleusercontent.com` suffix carries no information — every
	 * client has it — so it is kept for shape, and the identifying part is
	 * reduced to its first and last few characters.
	 *
	 * Short or unrecognised values are masked whole rather than partially: the
	 * fewer characters there are, the more a partial reveal gives away.
	 *
	 * @param string $client_id The stored client id.
	 * @return string A display-safe rendering.
	 */
	public static function mask_client_id( string $client_id ): string {
		$suffix = '.apps.googleusercontent.com';
		$local  = $client_id;
		$tail   = '';

		if ( str_ends_with( $client_id, $suffix ) ) {
			$local = substr( $client_id, 0, -strlen( $suffix ) );
			$tail  = $suffix;
		}

		if ( strlen( $local ) <= 12 ) {
			return str_repeat( '•', max( 8, strlen( $local ) ) ) . $tail;
		}

		return substr( $local, 0, 6 ) . '••••••' . substr( $local, -4 ) . $tail;
	}

	/**
	 * Lightweight competitor-detection list for instant banner paint. Cached in a
	 * short transient; full plans are fetched from /import/sources on demand.
	 *
	 * @return array<int,array{id:string,label:string,supports_adopt:bool}>
	 */
	private function detected_sources(): array {
		$cached = get_transient( 'storesheet_detected_sources' );
		if ( is_array( $cached ) ) {
			return $cached;
		}

		$state = get_option( 'storesheet_import_state', array() );
		if ( ! empty( $state['dismissed'] ) ) {
			return array(); // already imported/dismissed — do not nag.
		}

		$sources = ( new \StoreSheet\Importer\Importer() )->detected_summaries();
		set_transient( 'storesheet_detected_sources', $sources, 15 * MINUTE_IN_SECONDS );
		return $sources;
	}

	private function get_script_deps( string $asset_file ): array {
		if ( file_exists( $asset_file ) ) {
			$asset = require $asset_file;
			return $asset['dependencies'] ?? array();
		}
		return array( 'wp-element', 'wp-api-fetch', 'wp-i18n', 'wp-components' );
	}

	private function asset_version( string $file ): string {
		return file_exists( $file ) ? (string) filemtime( $file ) : STORESHEET_VERSION;
	}
}
