<?php
/**
 * Safe admin CSS generation from color schemes.
 *
 * This generator mirrors the structure and specificity of WordPress core
 * admin color schemes (wp-admin/css/colors/<scheme>/colors.css). It only
 * emits color-related declarations and uses the same low-specificity
 * selectors WordPress uses, so it coexists with plugin CSS exactly the way
 * a native admin color scheme does (no layout changes, no specificity wars).
 *
 * The WordPress theme color CSS variables are set on the body so that the
 * hero and any PowerSuite component consuming var(--wp-admin-theme-color)
 * follow the active scheme, identical to how WordPress drives its own UI.
 *
 * @package WP_PowerSuite\AdminColorCustomizer
 */

namespace WP_PowerSuite\AdminColorCustomizer;

use WP_PowerSuite\Core\AdminAppearance;

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

class CSS_Generator {

	private const BODY_SCOPE = 'body.wpps-acc-enabled';

	/**
	 * Properties that must never appear in generated admin color CSS.
	 * Guards against accidentally shipping layout/typography declarations.
	 */
	public const FORBIDDEN_CSS_PROPERTIES = [
		'width',
		'height',
		'min-width',
		'min-height',
		'padding',
		'margin',
		'font-size',
		'line-height',
		'font-weight',
		'border-width',
		'border-radius',
		'display',
		'align-items',
		'justify-content',
		'gap',
		'text-transform',
		'flex',
		'grid',
		'position',
		'top',
		'bottom',
		'transform',
		'opacity',
	];

	/**
	 * @param array<string, mixed>        $scheme
	 * @param array<string, mixed>        $settings
	 * @param 'admin'|'admin_bar'|'login' $context
	 * @param bool                          $scoped   When true, gate the theme variables behind body.wpps-acc-enabled.
	 */
	public function generate( array $scheme, array $settings, string $context = 'admin', bool $scoped = true ): string {
		$css = match ( $context ) {
			'admin_bar' => $this->generate_admin_bar_css( $scheme, $settings ),
			'login'     => $this->generate_login_css( $scheme ),
			default     => $this->generate_admin_css( $scheme, $settings, $scoped ),
		};

		return apply_filters( 'wpps_admin_color_customizer_css', $css, $scheme, $settings, $context );
	}

	/**
	 * @param array<string, mixed> $scheme
	 * @param array<string, mixed> $settings
	 */
	private function generate_admin_css( array $scheme, array $settings, bool $scoped = true ): string {
		$c       = $this->esc_colors( $scheme );
		$primary = AdminAppearance::wordpressThemePalette( (string) ( $scheme['primary'] ?? '#2271b1' ) );
		$var_scope = $scoped ? self::BODY_SCOPE : 'body';

		$css  = "/* WP PowerSuite Admin Color Customizer */\n";
		$css .= $this->theme_variables_block( $primary, $var_scope );
		$css .= $this->links_block( $c, $primary );
		$css .= $this->forms_block( $c );
		$css .= $this->buttons_block( $c, $primary );
		$css .= $this->wp_ui_block( $c );
		$css .= $this->list_tables_block( $c, $primary );
		$css .= $this->admin_menu_block( $c );
		$css .= $this->admin_bar_block( $c );
		$css .= $this->notices_block( $c );

		if ( ! empty( $settings['allow_custom_css'] ) && ! empty( $settings['custom_css'] ) ) {
			$css .= "\n" . Settings::sanitize_custom_css( (string) $settings['custom_css'] );
		}

		if ( ! empty( $scheme['custom_css'] ) && is_string( $scheme['custom_css'] ) ) {
			$css .= "\n" . Settings::sanitize_custom_css( $scheme['custom_css'] );
		}

		return $css;
	}

	/**
	 * Chrome-only CSS for the module's own settings page.
	 *
	 * The settings UI keeps native WordPress styling for its controls (buttons,
	 * inputs, list tables) so they stay predictable, but the admin menu, admin
	 * bar, links and theme variables ARE coloured so the user can see the active
	 * scheme take effect while customising. Generated unscoped (theme variables
	 * on `body`) because the module screen does not get the wpps-acc-enabled
	 * body class.
	 *
	 * @param array<string, mixed> $scheme
	 */
	public function generate_admin_chrome( array $scheme ): string {
		$c       = $this->esc_colors( $scheme );
		$primary = AdminAppearance::wordpressThemePalette( (string) ( $scheme['primary'] ?? '#2271b1' ) );

		$css  = "/* WP PowerSuite Admin Color Customizer (chrome) */\n";
		$css .= $this->theme_variables_block( $primary, 'body' );
		$css .= $this->links_block( $c, $primary );
		$css .= $this->admin_menu_block( $c );
		$css .= $this->admin_bar_block( $c );
		$css .= $this->notices_block( $c );

		return apply_filters( 'wpps_admin_color_customizer_css', $css, $scheme, [], 'admin_chrome' );
	}

	/**
	 * Feed the WordPress admin theme color variables. PowerSuite components
	 * (hero gradient, branded buttons) consume these, so they follow the
	 * scheme automatically — the same mechanism WordPress uses for its own UI.
	 *
	 * @param array<string, string> $palette
	 */
	private function theme_variables_block( array $palette, string $scope ): string {
		return sprintf(
			"%1\$s {\n\t--wp-admin-theme-color:%2\$s;\n\t--wp-admin-theme-color--rgb:%3\$s;\n\t--wp-admin-theme-color-darker-10:%4\$s;\n\t--wp-admin-theme-color-darker-10--rgb:%5\$s;\n\t--wp-admin-theme-color-darker-20:%6\$s;\n\t--wp-admin-theme-color-darker-20--rgb:%7\$s;\n}\n\n",
			$scope,
			esc_attr( $palette['color'] ),
			esc_attr( $palette['rgb'] ),
			esc_attr( $palette['darker_10'] ),
			esc_attr( $palette['darker_10_rgb'] ),
			esc_attr( $palette['darker_20'] ),
			esc_attr( $palette['darker_20_rgb'] )
		);
	}

	/**
	 * @param array<string, string> $c
	 * @param array<string, string> $primary
	 */
	private function links_block( array $c, array $primary ): string {
		return <<<CSS
a {
	color: {$c['primary']};
}
a:hover,
a:active,
a:focus {
	color: {$primary['darker_10']};
}
.wp-core-ui .button-link {
	color: {$c['primary']};
}
.wp-core-ui .button-link:hover,
.wp-core-ui .button-link:active,
.wp-core-ui .button-link:focus {
	color: {$primary['darker_20']};
}

CSS;
	}

	/**
	 * Form focus state mirrors WordPress core. Checkbox/radio checked states
	 * are driven by the --wp-admin-theme-color variable set above.
	 *
	 * @param array<string, string> $c
	 */
	private function forms_block( array $c ): string {
		return <<<CSS
input[type=text]:focus,
input[type=password]:focus,
input[type=color]:focus,
input[type=date]:focus,
input[type=datetime]:focus,
input[type=datetime-local]:focus,
input[type=email]:focus,
input[type=month]:focus,
input[type=number]:focus,
input[type=search]:focus,
input[type=tel]:focus,
input[type=time]:focus,
input[type=url]:focus,
input[type=week]:focus,
input[type=checkbox]:focus,
input[type=radio]:focus,
select:focus,
textarea:focus {
	border-color: {$c['primary']};
	box-shadow: 0 0 0 1px {$c['primary']};
}

CSS;
	}

	/**
	 * Faithful reproduction of WordPress core colors.css button section.
	 *
	 * This is required because the module dequeues the core `colors`
	 * stylesheet: without these rules, secondary/plain buttons fall back to
	 * the hardcoded blue in buttons.css and clash with a non-blue scheme.
	 * Mirroring core keeps secondary buttons consistent with the scheme,
	 * exactly like the built-in Midnight/Ocean schemes do.
	 *
	 * @param array<string, string> $c
	 * @param array<string, string> $primary
	 */
	private function buttons_block( array $c, array $primary ): string {
		return <<<CSS
.wp-core-ui .button,
.wp-core-ui .button-secondary {
	color: {$c['primary']};
	border-color: {$c['primary']};
}
.wp-core-ui .button.hover,
.wp-core-ui .button:hover,
.wp-core-ui .button-secondary:hover {
	border-color: {$primary['darker_10']};
	color: {$primary['darker_10']};
}
.wp-core-ui .button.focus,
.wp-core-ui .button:focus,
.wp-core-ui .button-secondary:focus {
	border-color: {$c['primary']};
	color: {$primary['darker_20']};
	box-shadow: 0 0 0 1px {$c['primary']};
}
.wp-core-ui .button:active {
	border-color: {$primary['darker_10']};
	box-shadow: none;
}
.wp-core-ui .button-group > .button.active {
	border-color: {$c['primary']};
}
.wp-core-ui .button-primary {
	background: {$c['button_bg']};
	border-color: {$c['button_border']};
	color: {$c['button_text']};
}
.wp-core-ui .button-primary:hover,
.wp-core-ui .button-primary:focus {
	background: {$c['button_hover_bg']};
	border-color: {$c['button_hover_border']};
	color: {$c['button_hover_text']};
}
.wp-core-ui .button-primary:focus {
	box-shadow: 0 0 0 1px #fff, 0 0 0 3px {$c['primary']};
}
.wp-core-ui .button-primary:active {
	background: {$primary['darker_20']};
	border-color: {$primary['darker_20']};
	color: {$c['button_text']};
}
.wp-core-ui .button-primary.active,
.wp-core-ui .button-primary.active:focus,
.wp-core-ui .button-primary.active:hover {
	background: {$c['button_bg']};
	color: {$c['button_text']};
	border-color: {$primary['darker_20']};
}

CSS;
	}

	/**
	 * @param array<string, string> $c
	 */
	private function wp_ui_block( array $c ): string {
		return <<<CSS
.wp-core-ui .wp-ui-primary {
	color: {$c['menu_text']};
	background-color: {$c['menu_bg']};
}
.wp-core-ui .wp-ui-text-primary {
	color: {$c['primary']};
}
.wp-core-ui .wp-ui-highlight {
	color: {$c['menu_hover_text']};
	background-color: {$c['menu_hover_bg']};
}
.wp-core-ui .wp-ui-text-highlight {
	color: {$c['accent']};
}
.wp-core-ui .wp-ui-notification {
	color: #fff;
	background-color: {$c['notice']};
}
.wp-core-ui .wp-ui-text-notification {
	color: {$c['notice']};
}

CSS;
	}

	/**
	 * @param array<string, string> $c
	 * @param array<string, string> $primary
	 */
	private function list_tables_block( array $c, array $primary ): string {
		return <<<CSS
.wrap .page-title-action,
.wrap .page-title-action:active {
	border: 1px solid {$c['primary']};
	color: {$c['primary']};
}
.wrap .page-title-action:hover {
	color: {$primary['darker_10']};
	border-color: {$primary['darker_10']};
}
.wrap .page-title-action:focus {
	border-color: {$c['primary']};
	color: {$primary['darker_20']};
	box-shadow: 0 0 0 1px {$c['primary']};
}
.view-switch a.current:before {
	color: {$c['primary']};
}
.view-switch a:hover:before {
	color: {$c['accent']};
}

CSS;
	}

	/**
	 * @param array<string, string> $c
	 */
	private function admin_menu_block( array $c ): string {
		return <<<CSS
#adminmenuback,
#adminmenuwrap,
#adminmenu {
	background: {$c['menu_bg']};
}
#adminmenu a {
	color: {$c['menu_text']};
}
#adminmenu div.wp-menu-image:before {
	color: {$c['menu_icon']};
}
#adminmenu a:hover,
#adminmenu li.menu-top:hover,
#adminmenu li.opensub > a.menu-top,
#adminmenu li > a.menu-top:focus {
	color: {$c['menu_hover_text']};
	background-color: {$c['menu_hover_bg']};
}
#adminmenu li.menu-top:hover div.wp-menu-image:before,
#adminmenu li.opensub > a.menu-top div.wp-menu-image:before {
	color: {$c['menu_hover_icon']};
}
#adminmenu .wp-submenu,
#adminmenu .wp-has-current-submenu .wp-submenu,
#adminmenu .wp-has-current-submenu.opensub .wp-submenu,
#adminmenu a.wp-has-current-submenu:focus + .wp-submenu {
	background: {$c['menu_bg']};
}
#adminmenu .wp-submenu a,
#adminmenu .wp-has-current-submenu .wp-submenu a,
#adminmenu a.wp-has-current-submenu:focus + .wp-submenu a,
#adminmenu .wp-has-current-submenu.opensub .wp-submenu a {
	color: {$c['menu_text']};
}
#adminmenu .wp-submenu a:focus,
#adminmenu .wp-submenu a:hover,
#adminmenu .wp-has-current-submenu .wp-submenu a:focus,
#adminmenu .wp-has-current-submenu .wp-submenu a:hover,
#adminmenu a.wp-has-current-submenu:focus + .wp-submenu a:focus,
#adminmenu a.wp-has-current-submenu:focus + .wp-submenu a:hover {
	color: {$c['menu_hover_text']};
}
#adminmenu li.current a.menu-top,
#adminmenu li.wp-has-current-submenu a.wp-has-current-submenu,
#adminmenu li.wp-has-current-submenu .wp-submenu .wp-submenu-head,
.folded #adminmenu li.current.menu-top {
	color: {$c['menu_current_text']};
	background: {$c['menu_current_bg']};
}
#adminmenu li.wp-has-current-submenu div.wp-menu-image:before,
#adminmenu a.current:hover div.wp-menu-image:before,
#adminmenu li.current div.wp-menu-image:before,
#adminmenu li.wp-has-current-submenu a:focus div.wp-menu-image:before,
#adminmenu li.wp-has-current-submenu.opensub div.wp-menu-image:before,
#adminmenu li:hover div.wp-menu-image:before,
#adminmenu li a:focus div.wp-menu-image:before,
#adminmenu li.opensub div.wp-menu-image:before {
	color: {$c['menu_current_icon']};
}
#adminmenu .menu-counter,
#adminmenu .awaiting-mod,
#adminmenu .update-plugins {
	color: #fff;
	background: {$c['notice']};
}
#collapse-button {
	color: {$c['menu_icon']};
}
#collapse-button:hover,
#collapse-button:focus {
	color: {$c['accent']};
}

CSS;
	}

	/**
	 * @param array<string, string> $c
	 */
	private function admin_bar_block( array $c ): string {
		return <<<CSS
#wpadminbar {
	color: {$c['toolbar_text']};
	background: {$c['toolbar_bg']};
}
#wpadminbar .ab-item,
#wpadminbar a.ab-item,
#wpadminbar > #wp-toolbar span.ab-label,
#wpadminbar > #wp-toolbar span.noticon {
	color: {$c['toolbar_text']};
}
#wpadminbar .ab-icon,
#wpadminbar .ab-icon:before,
#wpadminbar .ab-item:before,
#wpadminbar .ab-item:after {
	color: {$c['adminbar_icon']};
}
#wpadminbar:not(.mobile) .ab-top-menu > li:hover > .ab-item,
#wpadminbar:not(.mobile) .ab-top-menu > li > .ab-item:focus,
#wpadminbar.nojq .quicklinks .ab-top-menu > li > .ab-item:focus,
#wpadminbar.nojs .ab-top-menu > li.menupop:hover > .ab-item,
#wpadminbar .ab-top-menu > li.menupop.hover > .ab-item {
	color: {$c['menu_hover_text']};
	background: {$c['menu_hover_bg']};
}
#wpadminbar:not(.mobile) > #wp-toolbar li:hover span.ab-label,
#wpadminbar:not(.mobile) > #wp-toolbar li.hover span.ab-label,
#wpadminbar:not(.mobile) > #wp-toolbar a:focus span.ab-label {
	color: {$c['menu_hover_text']};
}
#wpadminbar:not(.mobile) li:hover .ab-icon:before,
#wpadminbar:not(.mobile) li:hover .ab-item:before,
#wpadminbar:not(.mobile) li:hover .ab-item:after {
	color: {$c['menu_hover_text']};
}
#wpadminbar .menupop .ab-sub-wrapper {
	background: {$c['toolbar_bg']};
}
#wpadminbar .ab-submenu .ab-item,
#wpadminbar .quicklinks .menupop ul li a,
#wpadminbar .quicklinks .menupop.hover ul li a,
#wpadminbar.nojs .quicklinks .menupop:hover ul li a {
	color: {$c['toolbar_text']};
}
#wpadminbar .quicklinks .menupop ul li a:hover,
#wpadminbar .quicklinks .menupop ul li a:focus,
#wpadminbar .quicklinks .menupop.hover ul li a:hover,
#wpadminbar .quicklinks .menupop.hover ul li a:focus {
	color: {$c['menu_hover_text']};
	background: {$c['menu_hover_bg']};
}

CSS;
	}

	/**
	 * @param array<string, string> $c
	 */
	private function notices_block( array $c ): string {
		return <<<CSS
#wpbody-content .notice-info {
	border-left-color: {$c['primary']};
}

CSS;
	}

	/**
	 * @param array<string, mixed> $scheme
	 * @param array<string, mixed> $settings
	 */
	private function generate_admin_bar_css( array $scheme, array $settings ): string {
		$c   = $this->esc_colors( $scheme );
		$css = $this->admin_bar_block( $c );

		$env = is_array( $settings['environment_badge'] ?? null ) ? $settings['environment_badge'] : [];
		if ( ! empty( $env['enabled'] ) && ! empty( $env['apply_bar_color'] ) ) {
			$badge_color = esc_attr( Scheme_Registry::sanitize_hex_color( $env['color'] ?? '#cc0000' ) ?? '#cc0000' );
			$css        .= "\n#wpadminbar { background-color: {$badge_color}; }";
		}

		return "/* WP PowerSuite Admin Bar */\n" . $css;
	}

	/**
	 * @param array<string, mixed> $scheme
	 */
	private function generate_login_css( array $scheme ): string {
		$c       = $this->esc_colors( $scheme );
		$primary = AdminAppearance::wordpressThemePalette( (string) ( $scheme['primary'] ?? '#2271b1' ) );
		$vars    = $this->inline_theme_variables( $primary );

		return <<<CSS
/* WP PowerSuite Login Screen */
body.login {
	{$vars}
}
body.login #loginform {
	border-color: {$c['primary']};
}
body.login .button-primary {
	background: {$c['button_bg']};
	border-color: {$c['button_border']};
	color: {$c['button_text']};
}
body.login #nav a,
body.login #backtoblog a {
	color: {$c['accent']};
}
CSS;
	}

	/**
	 * @param array<string, string> $palette
	 */
	private function inline_theme_variables( array $palette ): string {
		return sprintf(
			'--wp-admin-theme-color:%1$s;--wp-admin-theme-color--rgb:%2$s;--wp-admin-theme-color-darker-10:%3$s;--wp-admin-theme-color-darker-20:%4$s;',
			esc_attr( $palette['color'] ),
			esc_attr( $palette['rgb'] ),
			esc_attr( $palette['darker_10'] ),
			esc_attr( $palette['darker_20'] )
		);
	}

	/**
	 * @param array<string, mixed> $scheme
	 * @return array<string, string>
	 */
	private function esc_colors( array $scheme ): array {
		$defaults = Settings::get_accessible_defaults();

		$base_keys = [
			'primary',
			'secondary',
			'accent',
			'menu_bg',
			'menu_text',
			'menu_hover_bg',
			'menu_hover_text',
			'toolbar_bg',
			'toolbar_text',
			'button_bg',
			'button_text',
			'notice',
		];

		$out = [];
		foreach ( $base_keys as $key ) {
			$fallback    = Scheme_Registry::sanitize_hex_color( $defaults[ $key ] ?? '#000000' ) ?? '#000000';
			$color       = Scheme_Registry::sanitize_hex_color( $scheme[ $key ] ?? $fallback ) ?? $fallback;
			$out[ $key ] = esc_attr( $color );
		}

		$out['menu_icon']         = $this->resolve_color( $scheme, 'menu_icon', $this->resolve_menu_icon_fallback( $scheme, $out ) );
		$out['menu_hover_icon']   = $this->resolve_color( $scheme, 'menu_hover_icon', $out['menu_hover_text'] );
		$out['menu_current_icon'] = $this->resolve_color( $scheme, 'menu_current_icon', $out['menu_hover_text'] );
		$out['menu_current_bg']   = $this->resolve_color( $scheme, 'menu_current_bg', $out['menu_hover_bg'] );
		$out['menu_current_text'] = $this->resolve_color( $scheme, 'menu_current_text', $out['menu_hover_text'] );
		$out['adminbar_icon']     = $this->resolve_color( $scheme, 'adminbar_icon', $this->resolve_adminbar_icon_fallback( $scheme, $out ) );
		$out['button_border']       = $this->resolve_color( $scheme, 'button_border', $out['secondary'] );
		$out['button_hover_bg']     = $this->resolve_color( $scheme, 'button_hover_bg', $out['secondary'] );
		$out['button_hover_text']   = $this->resolve_color( $scheme, 'button_hover_text', $out['button_text'] );
		$out['button_hover_border'] = $this->resolve_color( $scheme, 'button_hover_border', $out['secondary'] );

		return $out;
	}

	/**
	 * @param array<string, mixed>  $scheme
	 * @param array<string, string> $resolved
	 */
	private function resolve_menu_icon_fallback( array $scheme, array $resolved ): string {
		$accent  = Scheme_Registry::sanitize_hex_color( $scheme['accent'] ?? $resolved['accent'] ?? '' );
		$menu_bg = $resolved['menu_bg'] ?? '#000000';

		if ( $accent !== null && strtolower( $accent ) !== strtolower( $menu_bg ) ) {
			return esc_attr( $accent );
		}

		return $resolved['menu_text'];
	}

	/**
	 * @param array<string, mixed>  $scheme
	 * @param array<string, string> $resolved
	 */
	private function resolve_adminbar_icon_fallback( array $scheme, array $resolved ): string {
		$accent      = Scheme_Registry::sanitize_hex_color( $scheme['accent'] ?? $resolved['accent'] ?? '' );
		$toolbar_bg  = $resolved['toolbar_bg'] ?? '#000000';
		$toolbar_txt = $resolved['toolbar_text'] ?? '#ffffff';

		if ( $accent !== null && strtolower( $accent ) !== strtolower( $toolbar_bg ) ) {
			return esc_attr( $accent );
		}

		return $toolbar_txt;
	}

	/**
	 * @param array<string, mixed> $scheme
	 */
	private function resolve_color( array $scheme, string $key, string $fallback ): string {
		$color = Scheme_Registry::sanitize_hex_color( $scheme[ $key ] ?? '' );

		return esc_attr( $color ?? $fallback );
	}
}
