<?php
/**
 * Site-wide design tokens (minimum: layout, colors, typography).
 * Stored in wp_options, emitted as CSS custom properties.
 *
 * @package AuraBuilder
 */

declare(strict_types=1);

namespace AuraBuilder\Globals;

final class SiteGlobals {

	/** Unique option key (Plugin Review: avoid generic prefixes). */
	public const OPTION = 'aptb_site_globals';
	/** Legacy option key (pre-1.0.2). */
	public const OPTION_LEGACY = 'aura_builder_globals';

	/**
	 * @return array<string, mixed>
	 */
	public static function defaults(): array {
		return array(
			'layout'     => array(
				'content_max'   => '1200px',
				'content_width' => '90%',
				'column_gap'    => '24px',
			),
			'colors'     => array(
				'primary'    => '#7c3aed',
				'secondary'  => '#0f172a',
				'text'       => '#0f172a',
				'text_muted' => '#64748b',
				'bg'         => '#ffffff',
				'border'     => '#e2e8f0',
				'btn_text'   => '#ffffff',
			),
			'typography' => array(
				'font_body'    => '',
				'font_heading' => '',
				'h1'           => '2.5rem',
				'h2'           => '2rem',
				'h3'           => '1.5rem',
				'body'         => '1rem',
			),
			'radius'     => array(
				'md' => '8px',
			),
			// Header / Footer (mínimo — header_footer.txt).
			'chrome'     => array(
				'header_sticky'      => '0',
				'header_sticky_bg'   => '#ffffff',
				'header_transparent' => '0',
				'footer_bg'          => '',
				'footer_text'        => '',
				// Crédito “Powered by Auranet Builder” (rodapé público).
				'credit_text_color'  => '#4f46e5',
				'credit_bg_color'    => '#ffffff',
			),
			// custom_css removed — WP.org forbids arbitrary CSS injection (use Customizer).
		);
	}

	/**
	 * @return array<string, mixed>
	 */
	public static function get(): array {
		$raw = get_option( self::OPTION, null );
		if ( ! is_array( $raw ) ) {
			$legacy = get_option( self::OPTION_LEGACY, array() );
			$raw    = is_array( $legacy ) ? $legacy : array();
		}
		return self::normalize( $raw );
	}

	/**
	 * @param array<string, mixed> $data
	 * @return array<string, mixed>
	 */
	public static function save( array $data ): array {
		$clean = self::normalize( $data );
		update_option( self::OPTION, $clean, false );
		// Drop legacy option key after first save under unique name.
		if ( get_option( self::OPTION_LEGACY, null ) !== null ) {
			delete_option( self::OPTION_LEGACY );
		}
		return $clean;
	}

	/**
	 * @param array<string, mixed> $data
	 * @return array<string, mixed>
	 */
	public static function normalize( array $data ): array {
		$d = self::defaults();

		$layout = is_array( $data['layout'] ?? null ) ? $data['layout'] : array();
		$colors = is_array( $data['colors'] ?? null ) ? $data['colors'] : array();
		$typo   = is_array( $data['typography'] ?? null ) ? $data['typography'] : array();
		$radius = is_array( $data['radius'] ?? null ) ? $data['radius'] : array();
		$chrome = is_array( $data['chrome'] ?? null ) ? $data['chrome'] : array();

		$out_layout = array(
			'content_max'   => self::sanitize_css_size( (string) ( $layout['content_max'] ?? $d['layout']['content_max'] ), '1200px' ),
			'content_width' => self::sanitize_css_size( (string) ( $layout['content_width'] ?? $d['layout']['content_width'] ), '90%' ),
			'column_gap'    => self::sanitize_css_size( (string) ( $layout['column_gap'] ?? $d['layout']['column_gap'] ), '24px' ),
		);

		$out_colors = array();
		foreach ( $d['colors'] as $key => $def ) {
			$out_colors[ $key ] = self::sanitize_color( (string) ( $colors[ $key ] ?? $def ), $def );
		}

		$out_typo = array(
			'font_body'    => self::sanitize_font( (string) ( $typo['font_body'] ?? '' ) ),
			'font_heading' => self::sanitize_font( (string) ( $typo['font_heading'] ?? '' ) ),
			'h1'           => self::sanitize_css_size( (string) ( $typo['h1'] ?? $d['typography']['h1'] ), '2.5rem' ),
			'h2'           => self::sanitize_css_size( (string) ( $typo['h2'] ?? $d['typography']['h2'] ), '2rem' ),
			'h3'           => self::sanitize_css_size( (string) ( $typo['h3'] ?? $d['typography']['h3'] ), '1.5rem' ),
			'body'         => self::sanitize_css_size( (string) ( $typo['body'] ?? $d['typography']['body'] ), '1rem' ),
		);

		$out_radius = array(
			'md' => self::sanitize_css_size( (string) ( $radius['md'] ?? $d['radius']['md'] ), '8px' ),
		);

		$flag = static function ( $v, string $default = '0' ): string {
			if ( $v === null || $v === '' ) {
				return $default;
			}
			return ( $v === true || $v === 1 || $v === '1' || $v === 'true' || $v === 'yes' || $v === 'on' ) ? '1' : '0';
		};
		$out_chrome = array(
			'header_sticky'      => $flag( $chrome['header_sticky'] ?? null, '0' ),
			'header_sticky_bg'   => self::sanitize_color( (string) ( $chrome['header_sticky_bg'] ?? '#ffffff' ), '#ffffff' ),
			'header_transparent' => $flag( $chrome['header_transparent'] ?? null, '0' ),
			'footer_bg'          => self::sanitize_color( (string) ( $chrome['footer_bg'] ?? '' ), '' ),
			'footer_text'        => self::sanitize_color( (string) ( $chrome['footer_text'] ?? '' ), '' ),
			'credit_text_color'  => self::sanitize_color(
				(string) ( $chrome['credit_text_color'] ?? $d['chrome']['credit_text_color'] ),
				$d['chrome']['credit_text_color']
			),
			'credit_bg_color'    => self::sanitize_color(
				(string) ( $chrome['credit_bg_color'] ?? $d['chrome']['credit_bg_color'] ),
				$d['chrome']['credit_bg_color']
			),
		);
		// Empty optional colors stay empty.
		if ( trim( (string) ( $chrome['footer_bg'] ?? '' ) ) === '' ) {
			$out_chrome['footer_bg'] = '';
		}
		if ( trim( (string) ( $chrome['footer_text'] ?? '' ) ) === '' ) {
			$out_chrome['footer_text'] = '';
		}
		// Allow transparent keyword for credit bar background.
		$bg_raw = trim( (string) ( $chrome['credit_bg_color'] ?? '' ) );
		if ( strtolower( $bg_raw ) === 'transparent' ) {
			$out_chrome['credit_bg_color'] = 'transparent';
		}

		return array(
			'layout'     => $out_layout,
			'colors'     => $out_colors,
			'typography' => $out_typo,
			'radius'     => $out_radius,
			'chrome'     => $out_chrome,
		);
	}

	/**
	 * CSS custom properties + light base rules + optional custom CSS.
	 */
	public static function css(): string {
		$g = self::get();
		$c = $g['colors'];
		$t = $g['typography'];
		$l = $g['layout'];
		$r = $g['radius'];

		$vars = array(
			'--ab-color-primary'    => $c['primary'],
			'--ab-color-secondary'  => $c['secondary'],
			'--ab-color-text'       => $c['text'],
			'--ab-color-text-muted' => $c['text_muted'],
			'--ab-color-bg'         => $c['bg'],
			'--ab-color-border'     => $c['border'],
			'--ab-color-btn-text'   => $c['btn_text'],
			'--ab-content-max'      => $l['content_max'],
			'--ab-content-width'    => $l['content_width'],
			'--ab-column-gap'       => $l['column_gap'],
			'--ab-radius-md'        => $r['md'],
			'--ab-font-size-h1'     => $t['h1'],
			'--ab-font-size-h2'     => $t['h2'],
			'--ab-font-size-h3'     => $t['h3'],
			'--ab-font-size-body'   => $t['body'],
		);
		if ( $t['font_body'] !== '' ) {
			$vars['--ab-font-body'] = $t['font_body'];
		}
		if ( $t['font_heading'] !== '' ) {
			$vars['--ab-font-heading'] = $t['font_heading'];
		}

		$css = ':root{';
		foreach ( $vars as $k => $v ) {
			$css .= $k . ':' . $v . ';';
		}
		$css .= '}';

		// Base: content width on builder surfaces when no local override.
		$css .= '.ab-content-wrap,.ab-page-body{'
			. 'box-sizing:border-box;'
			. '}';
		// Boxed sections can use the global max via CSS var (opt-in class).
		$css .= '.ab-type-section.ab-section--boxed,'
			. '.ab-document .ab-type-section[data-layout="boxed"]{'
			. 'max-width:var(--ab-content-max,1200px);'
			. 'width:var(--ab-content-width,90%);'
			. 'margin-left:auto;margin-right:auto;'
			. '}';

		// Typography tokens on headings without local font-size.
		$css .= '.ab-heading.ab-h1,.ab-type-heading .ab-heading[data-tag="h1"]{font-size:var(--ab-font-size-h1,2.5rem);}';
		$css .= '.ab-heading.ab-h2,.ab-type-heading .ab-heading[data-tag="h2"]{font-size:var(--ab-font-size-h2,2rem);}';
		$css .= '.ab-heading.ab-h3,.ab-type-heading .ab-heading[data-tag="h3"]{font-size:var(--ab-font-size-h3,1.5rem);}';
		if ( $t['font_heading'] !== '' ) {
			$css .= '.ab-heading,.ab-type-heading .ab-heading{font-family:var(--ab-font-heading);}';
		}
		if ( $t['font_body'] !== '' ) {
			$css .= '.ab-text,.ab-document,.ab-content-wrap{font-family:var(--ab-font-body);font-size:var(--ab-font-size-body,1rem);}';
		} else {
			$css .= '.ab-text{font-size:var(--ab-font-size-body,1rem);}';
		}
		$css .= '.ab-document,.ab-content-wrap{color:var(--ab-color-text,#0f172a);}';

		// Buttons use global tokens unless StyleEngine sets inline color/bg.
		$css .= '.ab-btn{'
			. 'background:var(--ab-color-primary,#7c3aed);'
			. 'color:var(--ab-color-btn-text,#fff);'
			. 'border-radius:var(--ab-radius-md,8px);'
			. 'border:1px solid transparent;'
			. '}';
		$css .= '.ab-btn:hover{filter:brightness(0.95);}';

		// Header / Footer chrome (mínimo).
		$ch = is_array( $g['chrome'] ?? null ) ? $g['chrome'] : array();
		if ( ( $ch['header_sticky'] ?? '0' ) === '1' ) {
			$sticky_bg = (string) ( $ch['header_sticky_bg'] ?? '#ffffff' );
			$css      .= 'body.ab-builder-page .wp-site-blocks > header,'
				. 'body.ab-builder-page header.wp-block-template-part,'
				. 'body.ab-builder-page .site-header,'
				. 'body.ab-builder-page #masthead{'
				. 'position:sticky;top:0;z-index:500;'
				. 'background:' . $sticky_bg . ';'
				. 'box-shadow:0 2px 12px rgba(15,23,42,.08);'
				. '}';
		}
		if ( ( $ch['header_transparent'] ?? '0' ) === '1' ) {
			$css .= 'body.ab-builder-page .wp-site-blocks > header,'
				. 'body.ab-builder-page header.wp-block-template-part,'
				. 'body.ab-builder-page .site-header{'
				. 'position:absolute;left:0;right:0;top:0;z-index:500;background:transparent;'
				. '}';
		}
		if ( ! empty( $ch['footer_bg'] ) ) {
			$css .= 'body.ab-builder-page .wp-site-blocks > footer,'
				. 'body.ab-builder-page footer.wp-block-template-part,'
				. 'body.ab-builder-page .site-footer,'
				. 'body.ab-builder-page #colophon{background:' . $ch['footer_bg'] . ' !important;}';
		}
		if ( ! empty( $ch['footer_text'] ) ) {
			$css .= 'body.ab-builder-page .wp-site-blocks > footer,'
				. 'body.ab-builder-page footer.wp-block-template-part,'
				. 'body.ab-builder-page .site-footer{color:' . $ch['footer_text'] . ';}';
		}

		return $css;
	}

	public static function hooks(): void {
		add_action( 'wp_enqueue_scripts', array( self::class, 'enqueue_css' ), 5 );
		add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_css_admin' ), 5 );
	}

	public static function enqueue_css(): void {
		$css = self::css();
		if ( $css === '' ) {
			return;
		}
		\AuraBuilder\Support\Assets::inline_style( 'aptb-globals', $css );
	}

	/** Only on builder editor screen. */
	public static function enqueue_css_admin(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$page = isset( $_GET['page'] ) ? sanitize_key( (string) wp_unslash( (string) $_GET['page'] ) ) : '';
		if ( $page !== 'aura-builder' && strpos( $page, 'aura-builder' ) !== 0 ) {
			return;
		}
		$css = self::css();
		if ( $css === '' ) {
			return;
		}
		\AuraBuilder\Support\Assets::inline_style( 'aptb-globals-admin', $css );
	}

	private static function sanitize_color( string $raw, string $fallback ): string {
		$raw = trim( $raw );
		if ( $raw === '' ) {
			return $fallback;
		}
		if ( preg_match( '/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $raw ) ) {
			return strtolower( $raw );
		}
		if ( preg_match( '/^(rgb|rgba|hsl|hsla|var)\(/i', $raw ) ) {
			return sanitize_text_field( $raw );
		}
		return $fallback;
	}

	private static function sanitize_css_size( string $raw, string $fallback ): string {
		$raw = trim( $raw );
		if ( $raw === '' ) {
			return $fallback;
		}
		// 1200px, 90%, 1.5rem, 24, 1.2em
		if ( preg_match( '/^[\d.]+(%|px|rem|em|vh|vw)?$/i', $raw ) ) {
			if ( preg_match( '/^[\d.]+$/', $raw ) ) {
				return $raw . 'px';
			}
			return $raw;
		}
		return $fallback;
	}

	private static function sanitize_font( string $raw ): string {
		$raw = trim( $raw );
		if ( $raw === '' ) {
			return '';
		}
		// Allow simple font stacks / Google font names.
		$raw = preg_replace( '/[<>{};]/', '', $raw ) ?? '';
		if ( strlen( $raw ) > 120 ) {
			$raw = substr( $raw, 0, 120 );
		}
		return $raw;
	}
}
