<?php
/**
 * Admin Logo Module
 *
 * @package AdminLogo
 * @since 1.0.0
 */

namespace AdminLogo;

use WP_PowerSuite\Core\AbstractModule;

class Module extends AbstractModule {

	private const MODULE_ID      = 'admin-logo';
	private const MODULE_VERSION = '1.3.2';

	private const DEFAULT_MENU_WIDTH_PX = 160;

	private ?array $settings_cache = null;

	/** @var array<string, array{width:int,height:int}> */
	private array $logo_image_dims_cache = [];

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

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

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

	public function getDescription(): string {
		return __( 'Your logo in the admin bar and/or sidebar for a white-label dashboard.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return [ 'admin', 'frontend' ];
	}

	protected function registerHooks(): void {
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueueSettingsMedia' ] );
		add_action( 'admin_bar_menu', [ $this, 'replaceWpLogoIfEnabled' ], 201 );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueueAdminBarAssetsIfEnabled' ] );
		add_action( 'wp_enqueue_scripts', [ $this, 'enqueueAdminBarAssetsIfEnabled' ] );
		add_action( 'admin_head', [ $this, 'renderAdminMenuLogoIfEnabled' ] );
		add_action( 'admin_head', [ $this, 'renderReplaceHomeIconCss' ], 99 );
	}

	public function replaceWpLogoIfEnabled( \WP_Admin_Bar $wp_admin_bar ): void {
		if ( ! $this->shouldShowAdminBarLogo( $this->getSettings() ) ) {
			return;
		}

		$this->replaceWpLogo( $wp_admin_bar );
	}

	public function enqueueAdminBarAssetsIfEnabled( string $hook = '' ): void {
		if ( ! $this->shouldShowAdminBarLogo( $this->getSettings() ) ) {
			return;
		}

		$this->enqueueAdminBarAssets( $hook );
	}

	public function renderAdminMenuLogoIfEnabled(): void {
		if ( ! $this->shouldShowAdminMenuLogo( $this->getSettings() ) ) {
			return;
		}

		$this->renderAdminMenuLogo();
	}

	public function enqueueSettingsMedia( string $hook ): void {
		if ( $hook !== 'toplevel_page_powersuite' ) {
			return;
		}

		wp_enqueue_media();
	}

	public function shouldShowAdminBarLogo( array $settings ): bool {
		$url = $this->getAdminBarLogoUrl( $settings );
		if ( $url === '' || ! $this->isValidLogoUrl( $url ) ) {
			return false;
		}

		$location = $this->resolveLogoLocation( $settings );

		return match ( $location ) {
			'admin_bar'          => true,
			'admin_menu'         => false,
			'admin_bar_and_menu' => true,
			default              => true,
		};
	}

	public function shouldShowAdminMenuLogo( array $settings ): bool {
		if ( ! is_admin() ) {
			return false;
		}

		$url = $this->getAdminMenuLogoUrl( $settings );
		if ( $url === '' || ! $this->isValidLogoUrl( $url ) ) {
			return false;
		}

		$location = $this->resolveLogoLocation( $settings );

		return match ( $location ) {
			'admin_bar'          => false,
			'admin_menu'         => true,
			'admin_bar_and_menu' => true,
			default              => true,
		};
	}

	public function resolveLogoLocation( array $settings ): string {
		$location = $settings['logo_location'] ?? '';
		if ( in_array( $location, [ 'admin_bar', 'admin_menu', 'admin_bar_and_menu' ], true ) ) {
			return $location;
		}

		$bar_url  = trim( (string) ( $settings['admin_bar_logo_url'] ?? '' ) );
		$menu_url = trim( (string) ( $settings['admin_menu_logo_url'] ?? '' ) );

		if ( $bar_url !== '' && $menu_url !== '' ) {
			return 'admin_bar_and_menu';
		}
		if ( $menu_url !== '' ) {
			return 'admin_menu';
		}

		return 'admin_bar';
	}

	public function getAdminBarLogoUrl( array $settings ): string {
		return trim( (string) ( $settings['admin_bar_logo_url'] ?? '' ) );
	}

	public function getAdminMenuLogoUrl( array $settings ): string {
		if ( ! empty( $settings['use_same_logo_for_menu'] ) ) {
			return $this->getAdminBarLogoUrl( $settings );
		}

		return trim( (string) ( $settings['admin_menu_logo_url'] ?? '' ) );
	}

	public function replaceWpLogo( \WP_Admin_Bar $wp_admin_bar ): void {
		if ( ! is_user_logged_in() || ! is_admin_bar_showing() ) {
			return;
		}

		if ( ! $wp_admin_bar->get_node( 'wp-logo' ) ) {
			return;
		}

		$settings = $this->getSettings();
		$logo_url = $this->getAdminBarLogoUrl( $settings );

		if ( $logo_url === '' || ! $this->isValidLogoUrl( $logo_url ) ) {
			return;
		}

		$this->removeWpLogoSubmenus( $wp_admin_bar );

		$logo_url  = esc_url( $this->normalizeLogoUrl( $logo_url ) );
		$link_url  = $this->getAdminBarLinkUrl( $settings );
		$logo_size = $this->getAdminBarLogoSizePx( $settings );

		$wp_admin_bar->add_node(
			[
				'id'    => 'wp-logo',
				'title' => sprintf(
					'<img src="%1$s" alt="%2$s" class="wpps-admin-bar-logo" width="%3$d" height="%3$d" />',
					$logo_url,
					esc_attr( get_bloginfo( 'name' ) ),
					$logo_size
				),
				'href'  => esc_url( $link_url ),
				'meta'  => [
					'class'      => 'wpps-admin-logo',
					'title'      => esc_attr__( 'Visit the homepage', 'wp-powersuite' ),
					'menu_title' => esc_attr__( 'Visit the homepage', 'wp-powersuite' ),
				],
			]
		);
	}

	/**
	 * Remove default WordPress logo submenus so the node behaves as a simple link.
	 */
	private function removeWpLogoSubmenus( \WP_Admin_Bar $wp_admin_bar ): void {
		$child_ids = [
			'about',
			'contribute',
			'wporg',
			'documentation',
			'learn',
			'support-forums',
			'feedback',
			'wp-logo-external',
		];

		foreach ( $child_ids as $id ) {
			$wp_admin_bar->remove_node( $id );
		}
	}

	public function getAdminBarLinkUrl( array $settings ): string {
		if ( ! empty( $settings['link_to_dashboard'] ) ) {
			if ( ! is_admin() ) {
				return admin_url();
			}

			return home_url( '/' );
		}

		return home_url( '/' );
	}

	public function enqueueAdminBarAssets( string $hook = '' ): void {
		if ( ! is_user_logged_in() || ! is_admin_bar_showing() ) {
			return;
		}

		$settings = $this->getSettings();
		$logo_url = $this->getAdminBarLogoUrl( $settings );

		if ( $logo_url === '' || ! $this->isValidLogoUrl( $logo_url ) ) {
			return;
		}

		$logo_size = $this->getAdminBarLogoSizePx( $settings );
		$base      = defined( 'WP_POWERSUITE_PLUGIN_URL' ) ? WP_POWERSUITE_PLUGIN_URL : '';
		$url       = $base . 'modules/' . self::MODULE_ID . '/assets/admin-logo.css';

		wp_enqueue_style( 'wpps-admin-logo', $url, [], self::MODULE_VERSION );
		wp_add_inline_style(
			'wpps-admin-logo',
			sprintf(
				'#wpadminbar #wp-admin-bar-wp-logo .wpps-admin-bar-logo{width:%1$dpx;height:%1$dpx;max-width:%1$dpx;max-height:%1$dpx;}',
				$logo_size
			)
		);
	}

	public function renderAdminMenuLogo(): void {
		$settings = $this->getSettings();
		$logo_url = $this->getAdminMenuLogoUrl( $settings );

		if ( $logo_url === '' || ! $this->isValidLogoUrl( $logo_url ) ) {
			return;
		}

		$logo_url       = esc_url( $this->normalizeLogoUrl( $logo_url ) );
		$link_url       = $this->getAdminMenuLinkUrl( $settings );
		$link_target    = ( ! empty( $settings['admin_menu_link_target'] ) && $settings['admin_menu_link_target'] === 'same' ) ? '_self' : '_blank';
		$menu_width_px  = (int) apply_filters( 'wpps_admin_menu_width', self::DEFAULT_MENU_WIDTH_PX );
		$image_dims     = $this->getLogoImageDimensions( $logo_url );
		$logo_height_px = $this->getAdminMenuLogoReservedHeightPx( $menu_width_px, $image_dims );
		$logo_title     = esc_attr__( 'Visit the homepage', 'wp-powersuite' );
		$logo_alt       = esc_attr( get_bloginfo( 'name' ) );

		$img_dim_attr = '';
		if ( ! empty( $image_dims['width'] ) && ! empty( $image_dims['height'] ) ) {
			$img_dim_attr = sprintf(
				' width="%d" height="%d"',
				absint( $image_dims['width'] ),
				absint( $image_dims['height'] )
			);
		}

		if ( $link_url !== '' ) {
			$rel = $link_target === '_blank' ? ' rel="noopener noreferrer"' : '';
			$logo_markup = sprintf(
				'<a id="wpps-admin-menu-logo" href="%1$s" target="%2$s"%3$s title="%4$s" aria-label="%4$s"><img src="%5$s" alt="%6$s" class="wpps-admin-menu-logo"%7$s /></a>',
				esc_url( $link_url ),
				esc_attr( $link_target ),
				$rel,
				$logo_title,
				$logo_url,
				$logo_alt,
				$img_dim_attr
			);
		} else {
			$logo_markup = sprintf(
				'<span id="wpps-admin-menu-logo" class="wpps-admin-menu-logo-wrap" aria-label="%1$s"><img src="%2$s" alt="%3$s" class="wpps-admin-menu-logo"%4$s /></span>',
				$logo_title,
				$logo_url,
				$logo_alt,
				$img_dim_attr
			);
		}

		?>
		<script type="text/javascript" id="wpps-admin-menu-logo-script">
		jQuery(document).ready(function($) {
			if ($('#wpps-admin-menu-logo').length) {
				return;
			}

			var logoMarkup = <?php echo wp_json_encode( $logo_markup ); ?>;
			var $adminMenuWrap = $('#adminmenuwrap');

			if ($adminMenuWrap.length) {
				$adminMenuWrap.prepend(logoMarkup);
			}
		});
		</script>
		<style type="text/css" id="wpps-admin-menu-logo-css">
			:root {
				--wpps-admin-menu-logo-height: <?php echo esc_html( (string) $logo_height_px ); ?>px;
			}

			#adminmenu {
				padding-top: var(--wpps-admin-menu-logo-height);
			}

			#wpps-admin-menu-logo {
				box-sizing: border-box;
				position: absolute;
				top: 0;
				left: 0;
				z-index: 10;
				width: 100%;
				height: var(--wpps-admin-menu-logo-height);
				padding: 16px 16px 16px 10px;
				display: flex;
				align-items: center;
				transition: .25s;
				cursor: pointer;
				text-decoration: none;
			}

			#wpps-admin-menu-logo:hover {
				background: #2c3338;
			}

			#wpps-admin-menu-logo .wpps-admin-menu-logo {
				display: block;
				width: 100%;
				height: auto;
				max-height: calc(var(--wpps-admin-menu-logo-height) - 32px);
				object-fit: contain;
				margin: 0 auto;
			}

			.folded #wpps-admin-menu-logo {
				display: none;
			}

			.folded #adminmenu {
				padding-top: 0;
			}

			@media screen and (min-width: 783px) {
				#adminmenu {
					margin-top: 0;
				}
			}

			@media screen and (max-width: 960px) {
				#wpps-admin-menu-logo {
					display: none;
				}

				#adminmenu {
					padding-top: 0;
				}
			}
		</style>
		<?php
	}

	public function renderReplaceHomeIconCss(): void {
		$settings = $this->getSettings();
		if ( empty( $settings['replace_admin_bar_home_icon'] ) || ! is_admin() || ! function_exists( 'get_site_icon_url' ) ) {
			return;
		}

		$site_icon_url = get_site_icon_url( 64 );
		if ( empty( $site_icon_url ) ) {
			return;
		}

		?>
		<style type="text/css" id="wpps-admin-bar-site-icon-css">
			.wp-admin #wpadminbar #wp-admin-bar-site-name > .ab-item:before {
				content: "" !important;
				background-image: url("<?php echo esc_url( $site_icon_url ); ?>") !important;
				background-repeat: no-repeat !important;
				background-position: center !important;
				background-size: 20px 20px !important;
				width: 20px !important;
				height: 20px !important;
				display: inline-block !important;
				font: 0/0 a !important;
				speak: never !important;
				top: 6px !important;
			}
		</style>
		<?php
	}

	public function isValidLogoUrl( string $url ): bool {
		$url = trim( $url );
		if ( $url === '' ) {
			return false;
		}

		$normalized = $this->normalizeLogoUrl( $url );
		if ( $normalized === '' || ! filter_var( $normalized, FILTER_VALIDATE_URL ) ) {
			return false;
		}

		if ( attachment_url_to_postid( $normalized ) > 0 ) {
			return true;
		}

		$bases = $this->getAllowedLogoUrlBases();
		foreach ( $bases as $base ) {
			if ( str_starts_with( $normalized, $base . '/' ) || $normalized === $base ) {
				return true;
			}
		}

		return true;
	}

	public function normalizeLogoUrl( string $url ): string {
		$url = esc_url_raw( trim( $url ) );
		if ( $url === '' ) {
			return '';
		}

		$home_scheme = wp_parse_url( home_url(), PHP_URL_SCHEME );
		if ( is_string( $home_scheme ) && $home_scheme !== '' ) {
			$url = set_url_scheme( $url, $home_scheme );
		}

		return $url;
	}

	private function getAdminBarLogoSizePx( array $settings ): int {
		$logo_size = trim( (string) ( $settings['admin_bar_logo_size'] ?? '' ) );
		if ( $logo_size === '' ) {
			return 20;
		}

		return min( 30, max( 1, absint( $logo_size ) ) );
	}

	private function getAdminMenuLinkUrl( array $settings ): string {
		$type = $settings['admin_menu_link'] ?? 'home';
		if ( $type === 'none' ) {
			return '';
		}
		if ( $type === 'dashboard' ) {
			return admin_url();
		}
		if ( $type === 'custom' ) {
			$custom = esc_url_raw( $settings['admin_menu_link_custom'] ?? '' );
			return $custom !== '' ? $custom : home_url( '/' );
		}

		return home_url( '/' );
	}

	/**
	 * @return array{width:int,height:int}
	 */
	private function getLogoImageDimensions( string $logo_url ): array {
		if ( isset( $this->logo_image_dims_cache[ $logo_url ] ) ) {
			return $this->logo_image_dims_cache[ $logo_url ];
		}

		$dims = [
			'width'  => 0,
			'height' => 0,
		];

		$attachment_id = attachment_url_to_postid( $logo_url );
		if ( $attachment_id <= 0 ) {
			return $dims;
		}

		$meta = wp_get_attachment_metadata( $attachment_id );
		if ( is_array( $meta ) && ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) {
			$dims['width']  = absint( $meta['width'] );
			$dims['height'] = absint( $meta['height'] );
		}

		$this->logo_image_dims_cache[ $logo_url ] = $dims;

		return $dims;
	}

	/**
	 * @param array{width:int,height:int} $image_dims
	 */
	private function getAdminMenuLogoReservedHeightPx( int $menu_width_px, array $image_dims ): int {
		$menu_width_px = $menu_width_px > 0 ? $menu_width_px : self::DEFAULT_MENU_WIDTH_PX;

		$padding_left   = 10;
		$padding_right  = 16;
		$padding_top    = 16;
		$padding_bottom = 16;
		$min_height_px  = 28;

		$content_width_px = max( 1, $menu_width_px - ( $padding_left + $padding_right ) );
		$img_w            = absint( $image_dims['width'] ?? 0 );
		$img_h            = absint( $image_dims['height'] ?? 0 );

		if ( $img_w > 0 && $img_h > 0 ) {
			$img_render_h = ( $content_width_px * $img_h ) / $img_w;
		} else {
			$img_render_h = $content_width_px / 4;
		}

		return max( $min_height_px, (int) ceil( $img_render_h + $padding_top + $padding_bottom ) );
	}

	/**
	 * @return list<string>
	 */
	private function getAllowedLogoUrlBases(): array {
		$bases = [
			untrailingslashit( home_url() ),
			untrailingslashit( site_url() ),
			untrailingslashit( content_url() ),
			untrailingslashit( ( wp_upload_dir() )['baseurl'] ?? '' ),
		];

		$normalized = [];
		foreach ( array_filter( $bases ) as $base ) {
			$normalized[] = $this->normalizeLogoUrl( $base );
		}

		return array_values( array_unique( $normalized ) );
	}

	private function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}

		$settings = $this->moduleSettings()->all( [] );
		$defaults = $this->getDefaultSettings();

		$this->settings_cache = array_merge( $defaults, $settings );

		return $this->settings_cache;
	}

	public function getDefaultSettings(): array {
		return [
			'logo_location'                => 'admin_bar_and_menu',
			'admin_bar_logo_url'           => '',
			'admin_bar_logo_size'          => '',
			'admin_menu_logo_url'          => '',
			'admin_menu_logo_width'        => '',
			'use_same_logo_for_menu'       => false,
			'admin_menu_link'              => 'home',
			'admin_menu_link_custom'       => '',
			'admin_menu_link_target'       => 'blank',
			'link_to_dashboard'            => false,
			'replace_admin_bar_home_icon'  => false,
		];
	}
}
