<?php
/**
 * Frontend output: banner, shortcodes, CSS variables, and consent AJAX handlers.
 *
 * @package LetsCodeCookieBanner
 */

defined( 'ABSPATH' ) || exit;

/**
 * Handles all frontend output for the cookie consent banner.
 */
class LCCB_Frontend {

	/**
	 * Registers WordPress hooks for frontend functionality.
	 */
	public function __construct() {
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
		add_action( 'wp_footer', array( $this, 'render_banner' ), 5 );
		add_action( 'wp_ajax_lccb_save_consent', array( $this, 'save_consent' ) );
		add_action( 'wp_ajax_nopriv_lccb_save_consent', array( $this, 'save_consent' ) );
		add_action( 'wp_ajax_lccb_scan_cookies', array( $this, 'ajax_scan_cookies' ) );
		// Note: no nopriv hook — scan is restricted to logged-in admins only.
		add_shortcode( 'lccb_manage_cookies', array( $this, 'shortcode_manage_cookies' ) );
		add_shortcode( 'lccb_cookie_policy', array( $this, 'shortcode_cookie_policy' ) );
	}

	/**
	 * Enqueues frontend CSS and JS assets and registers all inline scripts via the WP API.
	 *
	 * Execution order in <head>:
	 *   1. lccb-attribution-proxy  — cookie-setter wrapper
	 *   2. lccb-yt-blocker         — YouTube iframe blocker
	 *   3. lccb-vimeo-blocker      — Vimeo iframe blocker (skips iframes with dnt=1)
	 * Main banner JS is loaded in the footer via lccb-banner.
	 */
	public function enqueue_assets() {
		$settings = get_option( 'lccb_settings', array() );
		if ( empty( $settings['banner_enabled'] ) ) {
			return;
		}

		$cookie_name = wp_json_encode( LCCB_COOKIE_NAME );

		// 1. Attribution proxy — wraps document.cookie setter; configurable:true lets the
		// Pro autoblocking script re-wrap it without a TypeError.
		wp_register_script( 'lccb-attribution-proxy', false, array(), null, false ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- inline-only virtual handle, no file to version
		wp_enqueue_script( 'lccb-attribution-proxy' );
		wp_add_inline_script(
			'lccb-attribution-proxy',
			$this->get_attribution_proxy_js( $cookie_name )
		);

		// 2. YouTube iframe blocker.
		wp_register_script( 'lccb-yt-blocker', false, array(), null, false ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- inline-only virtual handle, no file to version
		wp_enqueue_script( 'lccb-yt-blocker' );
		wp_add_inline_script(
			'lccb-yt-blocker',
			$this->get_yt_blocker_js( $cookie_name )
		);

		// 3. Vimeo iframe blocker — chains on top of the YT prototype interceptor.
		wp_register_script( 'lccb-vimeo-blocker', false, array(), null, false ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- inline-only virtual handle, no file to version
		wp_enqueue_script( 'lccb-vimeo-blocker' );
		wp_add_inline_script(
			'lccb-vimeo-blocker',
			$this->get_vimeo_blocker_js( $cookie_name )
		);

		wp_enqueue_style(
			'lccb-banner',
			LCCB_PLUGIN_URL . 'assets/css/lccb-banner.css',
			array(),
			LCCB_VERSION
		);

		$css = $this->build_css_vars( $settings );
		wp_add_inline_style( 'lccb-banner', $css );

		wp_enqueue_script(
			'lccb-banner',
			LCCB_PLUGIN_URL . 'assets/js/lccb-banner.js',
			array(),
			LCCB_VERSION,
			true
		);

		$t = $this->get_frontend_strings();
		wp_localize_script(
			'lccb-banner',
			'lccbSettings',
			array(
				'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
				'nonce'      => wp_create_nonce( 'lccb_save_consent' ),
				'scanNonce'  => current_user_can( 'manage_options' ) ? wp_create_nonce( 'lccb_scan_cookies' ) : '',
				'cookieName' => LCCB_COOKIE_NAME,
				'cookieDays' => LCCB_COOKIE_DAYS,
				'yt'         => array(
					'warn' => $t['yt_warn'],
					'play' => $t['yt_play'],
				),
				'vm'         => array(
					'warn' => $t['vm_warn'],
					'play' => $t['vm_play'],
				),
			)
		);
		do_action( 'lccb_frontend_enqueue_assets', $settings );
	}

	/**
	 * Returns the attribution-proxy inline JS (cookie-setter interceptor).
	 *
	 * @param string $cookie_name JSON-encoded cookie name constant.
	 * @return string
	 */
	private function get_attribution_proxy_js( $cookie_name ) {
		ob_start();
		?>
(function(){
var a=window._lccbAttribs=window._lccbAttribs||[];
var h=window.location.hostname;
var ck=<?php echo $cookie_name; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_json_encode output is safe for JS context ?>;
var dp=Object.getOwnPropertyDescriptor(Document.prototype,'cookie')||Object.getOwnPropertyDescriptor(HTMLDocument.prototype,'cookie');
if(!dp||!dp.set)return;
var os=dp.set;
try{Object.defineProperty(document,'cookie',{configurable:true,enumerable:true,
get:function(){return dp.get?dp.get.call(document):'';},
set:function(v){
try{
var st=(new Error()).stack||'';
var ls=st.split('\n');
for(var i=1;i<ls.length;i++){
var m=ls[i].match(/(https?:\/\/[^\s\)]+)/);
if(m&&m[1].indexOf(h)===-1){
var su=m[1].replace(/:\d+:\d+$/,'');
var cn=String(v).split('=')[0].trim();
if(su&&cn&&cn!==ck&&cn.length<=255)a.push({s:su,c:cn});
break;
}
}
}catch(e){}
os.call(this,v);
}});}catch(e){}
})();
		<?php
		return trim( ob_get_clean() );
	}

	/**
	 * Returns the YouTube iframe early-blocking inline JS.
	 *
	 * @param string $cookie_name JSON-encoded cookie name constant.
	 * @return string
	 */
	private function get_yt_blocker_js( $cookie_name ) {
		ob_start();
		?>
(function(){
var cn=<?php echo $cookie_name; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_json_encode output is safe for JS context ?>;
var m=document.cookie.match('(?:^|; )'+cn.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+'=([^;]*)');
if(m){try{var c=JSON.parse(decodeURIComponent(m[1]));if(c&&c.marketing)return;}catch(e){}}
var na=Element.prototype.setAttribute;
function isYt(s){return!!s&&/youtube(?:-nocookie)?\.com\/embed\//.test(s);}
try{
var d=Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype,'src');
if(d&&d.set){var ns=d.set;Object.defineProperty(HTMLIFrameElement.prototype,'src',{configurable:true,enumerable:true,get:d.get,set:function(v){if(isYt(v)){na.call(this,'data-lccb-yt-blocked',v);return;}ns.call(this,v);}});}
}catch(e){}
try{
HTMLIFrameElement.prototype.setAttribute=function(n,v){if(n==='src'&&isYt(v)){na.call(this,'data-lccb-yt-blocked',v);return;}na.call(this,n,v);};
}catch(e){}
})();
		<?php
		return trim( ob_get_clean() );
	}

	/**
	 * Returns the Vimeo iframe early-blocking inline JS.
	 *
	 * Vimeo iframes that already carry dnt=1 in their URL are left alone — they
	 * do not install tracking cookies, so no consent is needed.
	 * The prototype interceptors chain on top of the YouTube blocker (which runs
	 * first) because both use configurable:true on HTMLIFrameElement.prototype.
	 *
	 * @param string $cookie_name JSON-encoded cookie name constant.
	 * @return string
	 */
	private function get_vimeo_blocker_js( $cookie_name ) {
		ob_start();
		?>
(function(){
var cn=<?php echo $cookie_name; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_json_encode output is safe for JS context ?>;
var m=document.cookie.match('(?:^|; )'+cn.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+'=([^;]*)');
if(m){try{var c=JSON.parse(decodeURIComponent(m[1]));if(c&&c.marketing)return;}catch(e){}}
var na=Element.prototype.setAttribute;
function isVimeo(s){return!!s&&/player\.vimeo\.com\/video\//.test(s);}
function hasDnt(s){return/[?&]dnt=1/.test(s||'');}
function shouldBlock(s){return isVimeo(s)&&!hasDnt(s);}
try{
var d=Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype,'src');
if(d&&d.set){var ns=d.set;Object.defineProperty(HTMLIFrameElement.prototype,'src',{configurable:true,enumerable:true,get:d.get,set:function(v){if(shouldBlock(v)){na.call(this,'data-lccb-vm-blocked',v);return;}ns.call(this,v);}});}
}catch(e){}
try{
var ps=HTMLIFrameElement.prototype.setAttribute;
HTMLIFrameElement.prototype.setAttribute=function(n,v){if(n==='src'&&shouldBlock(v)){na.call(this,'data-lccb-vm-blocked',v);return;}ps.call(this,n,v);};
}catch(e){}
})();
		<?php
		return trim( ob_get_clean() );
	}

	/**
	 * Builds the CSS custom properties block from plugin design settings.
	 *
	 * @param array $settings Plugin settings array from the options table.
	 * @return string Inline CSS string.
	 */
	private function build_css_vars( $settings ) {
		$defaults = array(
			'bg_color'            => '#ffffff',
			'text_color'          => '#333333',
			'btn_bg'              => '#1a73e8',
			'btn_text'            => '#ffffff',
			'btn_border_color'    => '#1a73e8',
			'overlay_bg'          => '#f8f8f8',
			'always_active_color' => '#2e7d32',
			'fab_bg'              => '#1a73e8',
			'fab_icon_color'      => '#ffffff',
			'fab_border_color'    => '#1a73e8',
			'btn_border_radius'   => 8,
			'btn_padding_v'       => 10,
			'btn_padding_h'       => 18,
			'fab_icon_size'       => 22,
			'banner_left'         => 20,
			'banner_right'        => 20,
			'banner_bottom'       => 20,
			'banner_radius'       => 8,
			'font_size'           => 14,
			'table_header_bg'     => '#f5f5f5',
			'table_header_text'   => '#333333',
			'table_odd_bg'        => '#ffffff',
			'table_odd_text'      => '#000000',
			'table_even_bg'       => '#ffffff',
			'table_even_text'     => '#000000',
			'fab_position'        => 'bottom-left',
			'fab_bottom'          => 20,
			'fab_left'            => 20,
			'fab_right'           => 20,
			'fab_border_width'    => 0,
			'btn_border_width'    => 2,
			'fab_box_shadow'      => '0 2px 10px rgba(0,0,0,.25)',
		);
		$s        = apply_filters( 'lccb_design_vars', $defaults, $settings );

		// Backwards compat: migrate old single banner_margin to individual fields.
		if ( ! isset( $settings['banner_left'] ) && isset( $settings['banner_margin'] ) ) {
			$m                  = absint( $settings['banner_margin'] );
			$s['banner_left']   = $m;
			$s['banner_right']  = $m;
			$s['banner_bottom'] = $m;
		}

		$bg                  = esc_attr( $s['bg_color'] );
		$text                = esc_attr( $s['text_color'] );
		$btn_bg              = esc_attr( $s['btn_bg'] );
		$btn_text            = esc_attr( $s['btn_text'] );
		$btn_border          = esc_attr( $s['btn_border_color'] );
		$overlay             = esc_attr( $s['overlay_bg'] );
		$always_active_color = esc_attr( $s['always_active_color'] );
		$fab_bg              = esc_attr( $s['fab_bg'] );
		$fab_icon_color      = esc_attr( $s['fab_icon_color'] );
		$fab_border_color    = esc_attr( $s['fab_border_color'] );

		$btn_radius    = absint( $s['btn_border_radius'] );
		$btn_pad_v     = absint( $s['btn_padding_v'] );
		$btn_pad_h     = absint( $s['btn_padding_h'] );
		$fab_icon      = absint( $s['fab_icon_size'] );
		$banner_left   = absint( $s['banner_left'] );
		$banner_right  = absint( $s['banner_right'] );
		$banner_bottom = absint( $s['banner_bottom'] );
		$banner_radius = absint( $s['banner_radius'] );
		$font_size     = absint( $s['font_size'] );
		if ( $font_size < 10 ) {
			$font_size = 14; }

		$table_header_bg   = esc_attr( $s['table_header_bg'] );
		$table_header_text = esc_attr( $s['table_header_text'] );
		$table_odd_bg      = esc_attr( $s['table_odd_bg'] );
		$table_odd_text    = esc_attr( $s['table_odd_text'] );
		$table_even_bg     = esc_attr( $s['table_even_bg'] );
		$table_even_text   = esc_attr( $s['table_even_text'] );

		$fab_position     = ( 'bottom-right' === $s['fab_position'] ) ? 'bottom-right' : 'bottom-left';
		$fab_bottom       = absint( $s['fab_bottom'] );
		$fab_left         = absint( $s['fab_left'] );
		$fab_right        = absint( $s['fab_right'] );
		$fab_border_width = absint( $s['fab_border_width'] );
		$btn_border_width = absint( $s['btn_border_width'] );
		$fab_box_shadow   = esc_attr( $s['fab_box_shadow'] );

		$css = ':root{'
			. "--lccb-bg:{$bg};"
			. "--lccb-text:{$text};"
			. "--lccb-btn-bg:{$btn_bg};"
			. "--lccb-btn-text:{$btn_text};"
			. "--lccb-btn-border:{$btn_border};"
			. "--lccb-overlay-bg:{$overlay};"
			. "--lccb-always-on-color:{$always_active_color};"
			. "--lccb-fab-bg:{$fab_bg};"
			. "--lccb-fab-color:{$fab_icon_color};"
			. "--lccb-fab-border:{$fab_border_color};"
			. "--lccb-btn-radius:{$btn_radius}px;"
			. "--lccb-btn-pad-v:{$btn_pad_v}px;"
			. "--lccb-btn-pad-h:{$btn_pad_h}px;"
			. "--lccb-fab-icon:{$fab_icon}px;"
			. "--lccb-banner-left:{$banner_left}px;"
			. "--lccb-banner-right:{$banner_right}px;"
			. "--lccb-banner-bottom:{$banner_bottom}px;"
			. "--lccb-banner-radius:{$banner_radius}px;"
			. "--lccb-font-size:{$font_size}px;"
			. "--lccb-table-header-bg:{$table_header_bg};"
			. "--lccb-table-header-text:{$table_header_text};"
			. "--lccb-table-odd-bg:{$table_odd_bg};"
			. "--lccb-table-odd-text:{$table_odd_text};"
			. "--lccb-table-even-bg:{$table_even_bg};"
			. "--lccb-table-even-text:{$table_even_text};"
			. "--lccb-fab-border-width:{$fab_border_width}px;"
			. "--lccb-btn-border-width:{$btn_border_width}px;"
			. "--lccb-fab-shadow:{$fab_box_shadow};"
			. "--lccb-fab-bottom:{$fab_bottom}px;"
			. ( 'bottom-right' === $fab_position
				? "--lccb-fab-left:auto;--lccb-fab-right:{$fab_right}px;"
				: "--lccb-fab-left:{$fab_left}px;--lccb-fab-right:auto;" )
			. '}';

		return $css;
	}

	/**
	 * Returns 'it' for Italian locales, 'en' for everything else.
	 */
	private function get_locale_group() {
		return ( strpos( get_locale(), 'it' ) === 0 ) ? 'it' : 'en';
	}

	/**
	 * Returns all frontend-visible strings in the correct language.
	 */
	private function get_frontend_strings() {
		if ( 'it' === $this->get_locale_group() ) {
			return array(
				'close_banner_label'   => 'Chiudi il banner e rifiuta i cookie non necessari',
				'banner_title'         => 'Informativa sui Cookie',
				'banner_text'          => 'La tua privacy è importante per noi. Questo sito utilizza cookie tecnici necessari per garantire il corretto funzionamento delle pagine. Con il tuo consenso, noi e i nostri partner selezionati utilizziamo anche cookie di profilazione e tracciamento (anche di terze parti) per scopi statistici, per mostrarti pubblicità personalizzata e per offrirti funzionalità social. Puoi accettare tutti i cookie cliccando su "Accetta tutto", rifiutarli mantenendo solo quelli necessari cliccando su "Rifiuta tutto" oppure personalizzare le tue scelte cliccando su "Rivedi preferenze". Chiudendo questo banner tramite la "X" in alto a destra, continuerai la navigazione con le sole impostazioni di default (cookie tecnici). Puoi revocare o modificare il tuo consenso in qualsiasi momento tramite il link "Gestisci cookie" presente nel footer del sito.',
				'policy_prefix'        => 'Per maggiori informazioni, consulta la nostra',
				'policy_link'          => 'Cookie Policy',
				'actions_label'        => 'Scelte sui cookie',
				'accept_all'           => 'Accetta tutto',
				'reject_all'           => 'Rifiuta tutto',
				'customize'            => 'Rivedi preferenze',
				'panel_title'          => 'Preferenze Cookie',
				'close_panel_label'    => 'Chiudi il pannello preferenze',
				'cat_necessary'        => 'Cookie Tecnici / Necessari',
				'cat_necessary_desc'   => 'Indispensabili per il corretto funzionamento del sito. Non possono essere disabilitati.',
				'always_active'        => 'Sempre attivi',
				'cat_statistics'       => 'Cookie Statistici / Analitici',
				'cat_statistics_desc'  => 'Ci aiutano a capire come i visitatori interagiscono con il sito raccogliendo informazioni in forma anonima (es. Google Analytics).',
				'enable_statistics'    => 'Abilita cookie statistici',
				'cat_marketing'        => 'Cookie di Marketing / Profilazione',
				'cat_marketing_desc'   => 'Utilizzati per mostrare pubblicità personalizzata e misurare l\'efficacia delle campagne. Possono essere impostati da terze parti.',
				'enable_marketing'     => 'Abilita cookie di marketing',
				'cat_preferences'      => 'Cookie di Preferenza / Funzionalità',
				'cat_preferences_desc' => 'Memorizzano le tue scelte (lingua, regione) per offrirti un\'esperienza di navigazione personalizzata.',
				'enable_preferences'   => 'Abilita cookie di preferenza',
				'save_preferences'     => 'Salva preferenze',
				'manage_cookies'       => 'Gestisci le preferenze cookie',
				'show_cookies'         => 'Mostra cookie (%d)',
				'hide_cookies'         => 'Nascondi cookie',
				'col_name'             => 'Cookie',
				'col_vendor'           => 'Fornitore',
				'col_duration'         => 'Scadenza',
				'col_purpose'          => 'Finalità',
				'policy_necessary'     => 'Cookie Tecnici / Necessari',
				'policy_statistics'    => 'Cookie Statistici / Analitici',
				'policy_marketing'     => 'Cookie di Marketing / Profilazione',
				'policy_preferences'   => 'Cookie di Preferenza / Funzionalità',
				'yt_warn'              => 'Premendo Play acconsenti ai cookie di marketing, profilazione e statistica di terze parti (es. YouTube/Google). Il video viene caricato da YouTube.',
				'yt_play'              => 'Riproduci video',
				'vm_warn'              => 'Premendo Play acconsenti ai cookie di marketing, profilazione e statistica di terze parti (es. Vimeo). Il video viene caricato da Vimeo.',
				'vm_play'              => 'Riproduci video',
			);
		}

		return array(
			'close_banner_label'   => 'Close banner and reject non-essential cookies',
			'banner_title'         => 'Cookie Notice',
			'banner_text'          => 'Your privacy is important to us. This site uses technical cookies that are necessary for the correct functioning of the pages. With your consent, we and our selected partners also use profiling and tracking cookies (including third-party) for statistical purposes, to show you personalised advertising and to offer you social features. You can accept all cookies by clicking "Accept all", reject them while keeping only the necessary ones by clicking "Reject all", or customise your choices by clicking "Review preferences". By closing this banner via the "X" in the top right corner, you will continue browsing with only the default settings (technical cookies). You can revoke or change your consent at any time via the "Manage cookies" link in the website footer.',
			'policy_prefix'        => 'For more information, please read our',
			'policy_link'          => 'Cookie Policy',
			'actions_label'        => 'Cookie choices',
			'accept_all'           => 'Accept all',
			'reject_all'           => 'Reject all',
			'customize'            => 'Review preferences',
			'panel_title'          => 'Cookie Preferences',
			'close_panel_label'    => 'Close cookie preferences panel',
			'cat_necessary'        => 'Technical / Necessary Cookies',
			'cat_necessary_desc'   => 'Essential for the correct functioning of the site. They cannot be disabled.',
			'always_active'        => 'Always active',
			'cat_statistics'       => 'Statistical / Analytics Cookies',
			'cat_statistics_desc'  => 'They help us understand how visitors interact with the site by collecting information anonymously (e.g. Google Analytics).',
			'enable_statistics'    => 'Enable statistics cookies',
			'cat_marketing'        => 'Marketing / Profiling Cookies',
			'cat_marketing_desc'   => 'Used to show personalised advertising and measure the effectiveness of campaigns. They may be set by third parties.',
			'enable_marketing'     => 'Enable marketing cookies',
			'cat_preferences'      => 'Preference / Functionality Cookies',
			'cat_preferences_desc' => 'They store your choices (language, region) to offer you a personalised browsing experience.',
			'enable_preferences'   => 'Enable preference cookies',
			'save_preferences'     => 'Save preferences',
			'manage_cookies'       => 'Manage cookie preferences',
			'show_cookies'         => 'Show cookies (%d)',
			'hide_cookies'         => 'Hide cookies',
			'col_name'             => 'Cookie',
			'col_vendor'           => 'Vendor',
			'col_duration'         => 'Duration',
			'col_purpose'          => 'Purpose',
			'policy_necessary'     => 'Technical / Necessary Cookies',
			'policy_statistics'    => 'Statistical / Analytics Cookies',
			'policy_marketing'     => 'Marketing / Profiling Cookies',
			'policy_preferences'   => 'Preference / Functionality Cookies',
			'yt_warn'              => 'By pressing Play you consent to marketing, profiling and analytics cookies from third parties (e.g. YouTube/Google). The video is loaded from YouTube.',
			'yt_play'              => 'Play video',
			'vm_warn'              => 'By pressing Play you consent to marketing, profiling and analytics cookies from third parties (e.g. Vimeo). The video is loaded from Vimeo.',
			'vm_play'              => 'Play video',
		);
	}

	/**
	 * Outputs the cookie consent banner HTML in the page footer.
	 */
	public function render_banner() {
		$settings = get_option( 'lccb_settings', array() );
		if ( empty( $settings['banner_enabled'] ) ) {
			return;
		}
		$policy_url = ! empty( $settings['cookie_policy_url'] ) ? esc_url( $settings['cookie_policy_url'] ) : '';
		$t          = $this->get_frontend_strings();
		?>

		<!-- Let's Code Cookie Banner -->
		<div id="lccb-banner" class="lccb-banner" role="dialog" aria-modal="true"
			aria-labelledby="lccb-title" aria-describedby="lccb-desc"
			aria-live="assertive" hidden>

			<div class="lccb-banner-inner">

				<button type="button" class="lccb-x-btn" id="lccb-x-btn"
						aria-label="<?php echo esc_attr( $t['close_banner_label'] ); ?>">
					<span aria-hidden="true">&times;</span>
				</button>

				<h2 id="lccb-title" class="lccb-title">
					<?php echo esc_html( $t['banner_title'] ); ?>
				</h2>

				<p id="lccb-desc" class="lccb-text">
					<?php echo esc_html( $t['banner_text'] ); ?>
					<?php if ( $policy_url ) : ?>
						<?php echo esc_html( $t['policy_prefix'] ); ?>
					<a href="<?php echo esc_url( $policy_url ); ?>" target="_blank" rel="noopener noreferrer" class="lccb-policy-link">
						<?php echo esc_html( $t['policy_link'] ); ?>
					</a>.
					<?php endif; ?>
				</p>

				<div class="lccb-actions" role="group"
					aria-label="<?php echo esc_attr( $t['actions_label'] ); ?>">
					<button type="button" id="lccb-accept-all" class="lccb-btn">
						<?php echo esc_html( $t['accept_all'] ); ?>
					</button>
					<button type="button" id="lccb-reject-all" class="lccb-btn">
						<?php echo esc_html( $t['reject_all'] ); ?>
					</button>
					<button type="button" id="lccb-customize" class="lccb-btn">
						<?php echo esc_html( $t['customize'] ); ?>
					</button>
				</div>

				<?php if ( ! empty( $settings['show_powered_by'] ) ) : ?>
				<p class="lccb-powered-by">
					<?php
					echo wp_kses(
						sprintf(
							/* translators: %s = linked "Let's Code" brand name. */
							__( 'Powered by %s', 'lets-code-cookie-banner' ),
							'<a href="https://let-scode.it" target="_blank" rel="noopener noreferrer">Let\'s Code</a>'
						),
						array(
							'a' => array(
								'href'   => true,
								'target' => true,
								'rel'    => true,
							),
						)
					);
					?>
				</p>
				<?php endif; ?>

			</div>
		</div><!-- /#lccb-banner -->

		<!-- Preferences panel overlay -->
		<div id="lccb-overlay" class="lccb-overlay" aria-hidden="true" hidden></div>

		<!-- Preferences panel -->
		<div id="lccb-panel" class="lccb-panel" role="dialog" aria-modal="true"
			aria-labelledby="lccb-panel-title" hidden>

			<div class="lccb-panel-header">
				<h2 id="lccb-panel-title"><?php echo esc_html( $t['panel_title'] ); ?></h2>
				<button type="button" class="lccb-x-btn" id="lccb-panel-close"
						aria-label="<?php echo esc_attr( $t['close_panel_label'] ); ?>">
					<span aria-hidden="true">&times;</span>
				</button>
			</div>

			<div class="lccb-panel-body">

				<!-- Necessari — sempre attivi, checkbox disabilitata -->
				<div class="lccb-category">
					<div class="lccb-category-info">
						<strong><?php echo esc_html( $t['cat_necessary'] ); ?></strong>
						<p><?php echo esc_html( $t['cat_necessary_desc'] ); ?></p>
						<?php $this->render_cookie_detail_list( 'necessary', $t ); ?>
					</div>
					<span class="lccb-always-on" aria-label="<?php echo esc_attr( $t['always_active'] ); ?>">
						<?php echo esc_html( $t['always_active'] ); ?>
					</span>
				</div>

				<!-- Statistiche -->
				<div class="lccb-category">
					<div class="lccb-category-info">
						<strong><?php echo esc_html( $t['cat_statistics'] ); ?></strong>
						<p><?php echo esc_html( $t['cat_statistics_desc'] ); ?></p>
						<?php $this->render_cookie_detail_list( 'statistics', $t ); ?>
					</div>
					<label class="lccb-toggle" for="lccb-chk-statistics">
						<input type="checkbox" id="lccb-chk-statistics" name="statistics" value="1">
						<span class="lccb-slider" aria-hidden="true"></span>
						<span class="lccb-sr"><?php echo esc_html( $t['enable_statistics'] ); ?></span>
					</label>
				</div>

				<!-- Marketing -->
				<div class="lccb-category">
					<div class="lccb-category-info">
						<strong><?php echo esc_html( $t['cat_marketing'] ); ?></strong>
						<p><?php echo esc_html( $t['cat_marketing_desc'] ); ?></p>
						<?php $this->render_cookie_detail_list( 'marketing', $t ); ?>
					</div>
					<label class="lccb-toggle" for="lccb-chk-marketing">
						<input type="checkbox" id="lccb-chk-marketing" name="marketing" value="1">
						<span class="lccb-slider" aria-hidden="true"></span>
						<span class="lccb-sr"><?php echo esc_html( $t['enable_marketing'] ); ?></span>
					</label>
				</div>

				<!-- Preferenze -->
				<div class="lccb-category">
					<div class="lccb-category-info">
						<strong><?php echo esc_html( $t['cat_preferences'] ); ?></strong>
						<p><?php echo esc_html( $t['cat_preferences_desc'] ); ?></p>
						<?php $this->render_cookie_detail_list( 'preferences', $t ); ?>
					</div>
					<label class="lccb-toggle" for="lccb-chk-preferences">
						<input type="checkbox" id="lccb-chk-preferences" name="preferences" value="1">
						<span class="lccb-slider" aria-hidden="true"></span>
						<span class="lccb-sr"><?php echo esc_html( $t['enable_preferences'] ); ?></span>
					</label>
				</div>

			</div><!-- /.lccb-panel-body -->

			<div class="lccb-panel-footer">
				<?php if ( $policy_url ) : ?>
				<a href="<?php echo esc_url( $policy_url ); ?>" target="_blank" rel="noopener noreferrer" class="lccb-policy-link-sm">
					<?php echo esc_html( $t['policy_link'] ); ?>
				</a>
				<?php endif; ?>
				<div class="lccb-panel-btns">
					<button type="button" id="lccb-pref-reject" class="lccb-btn">
						<?php echo esc_html( $t['reject_all'] ); ?>
					</button>
					<button type="button" id="lccb-pref-save" class="lccb-btn">
						<?php echo esc_html( $t['save_preferences'] ); ?>
					</button>
					<button type="button" id="lccb-pref-accept" class="lccb-btn">
						<?php echo esc_html( $t['accept_all'] ); ?>
					</button>
				</div>
			</div>

		</div><!-- /#lccb-panel -->

		<!-- Floating "Gestisci cookie" button — visible after consent -->
		<?php if ( '0' !== ( $settings['show_fab_button'] ?? '1' ) ) : ?>
		<div id="lccb-fab" class="lccb-fab" hidden aria-live="polite">
			<button type="button" id="lccb-fab-btn" class="lccb-fab-btn"
					aria-label="<?php echo esc_attr( $t['manage_cookies'] ); ?>">
				<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="currentColor">
					<path d="M21.598 11.064a1.006 1.006 0 0 0-.854-.172A2.938 2.938 0 0 1 20 11c-1.654 0-3-1.346-3.003-2.937.005-.034.016-.136.017-.17a.999.999 0 0 0-1.254-1.006A2.99 2.99 0 0 1 15 7a3.003 3.003 0 0 1-3-3 2.99 2.99 0 0 1 .005-.176 1 1 0 0 0-1.726-.727A9.003 9.003 0 0 0 3 12c0 4.963 4.038 9 9 9s9-4.037 9-9c0-.248-.014-.494-.033-.739a1.002 1.002 0 0 0-.369-.197zM12 19c-3.86 0-7-3.14-7-7a7.008 7.008 0 0 1 5.906-6.91A5.007 5.007 0 0 0 15 9.002a4.942 4.942 0 0 0 .947-.091A4.954 4.954 0 0 0 20 13.003L20 13c0 3.86-3.14 7-7 7zm-3-7a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3-2a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/>
				</svg>
			</button>
		</div>
		<?php endif; ?>

		<?php
	}

	/**
	 * Renders the expandable cookie detail list inside the preferences panel.
	 * Called once per category from render_banner().
	 *
	 * @param string $category Category slug: necessary|statistics|marketing|preferences.
	 * @param array  $t        Localised frontend string map.
	 */
	private function render_cookie_detail_list( $category, $t ) {
		$cookies = LCCB_Cookie_Registry::get_by_category( $category );
		if ( empty( $cookies ) ) {
			return;
		}
		$count      = count( $cookies );
		$label_show = sprintf( $t['show_cookies'], $count );
		$label_hide = $t['hide_cookies'];
		?>
		<button type="button" class="lccb-cookie-toggle"
				aria-expanded="false"
				data-show="<?php echo esc_attr( $label_show ); ?>"
				data-hide="<?php echo esc_attr( $label_hide ); ?>">
			<?php echo esc_html( $label_show ); ?>
		</button>
		<div class="lccb-cookie-list" hidden>
			<table class="lccb-cookie-tbl">
				<thead>
					<tr>
						<th><?php echo esc_html( $t['col_name'] ); ?></th>
						<th><?php echo esc_html( $t['col_vendor'] ); ?></th>
						<th><?php echo esc_html( $t['col_duration'] ); ?></th>
						<th><?php echo esc_html( $t['col_purpose'] ); ?></th>
					</tr>
				</thead>
				<tbody>
				<?php foreach ( $cookies as $ck ) : ?>
					<tr>
						<td><code><?php echo esc_html( $ck['name'] ?? '' ); ?></code></td>
						<td><?php echo esc_html( $ck['vendor'] ?? '' ); ?></td>
						<td><?php echo esc_html( $ck['duration'] ?? '' ); ?></td>
						<td><?php echo esc_html( $ck['description'] ?? '' ); ?></td>
					</tr>
				<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<?php
	}

	/**
	 * Shortcode [lccb_manage_cookies] — renders the cookie preferences FAB button.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string
	 */
	public function shortcode_manage_cookies( $atts ) {
		$atts = shortcode_atts(
			array(
				'class' => '',
			),
			$atts,
			'lccb_manage_cookies'
		);

		$extra_class = $atts['class'] ? ' ' . sanitize_html_class( $atts['class'] ) : '';
		$t           = $this->get_frontend_strings();

		$svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"><path d="M21.598 11.064a1.006 1.006 0 0 0-.854-.172A2.938 2.938 0 0 1 20 11c-1.654 0-3-1.346-3.003-2.937.005-.034.016-.136.017-.17a.999.999 0 0 0-1.254-1.006A2.99 2.99 0 0 1 15 7a3.003 3.003 0 0 1-3-3 2.99 2.99 0 0 1 .005-.176 1 1 0 0 0-1.726-.727A9.003 9.003 0 0 0 3 12c0 4.963 4.038 9 9 9s9-4.037 9-9c0-.248-.014-.494-.033-.739a1.002 1.002 0 0 0-.369-.197zM12 19c-3.86 0-7-3.14-7-7a7.008 7.008 0 0 1 5.906-6.91A5.007 5.007 0 0 0 15 9.002a4.942 4.942 0 0 0 .947-.091A4.954 4.954 0 0 0 20 13.003L20 13c0 3.86-3.14 7-7 7zm-3-7a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3-2a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/></svg>';

		return sprintf(
			'<span class="lccb-manage-link%s"><button type="button" class="lccb-fab-btn" onclick="if(window.lccbOpenPanel)window.lccbOpenPanel()" aria-label="%s">%s</button></span>',
			esc_attr( $extra_class ),
			esc_attr( $t['manage_cookies'] ),
			$svg
		);
	}

	/**
	 * Shortcode [lccb_cookie_policy] — renders the per-category cookie policy table.
	 *
	 * @param array $atts Shortcode attributes (unused, reserved for future use).
	 * @return string
	 */
	public function shortcode_cookie_policy( $atts ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- required by WordPress shortcode API
		// Ensure the plugin stylesheet is loaded even when the banner is disabled.
		if ( ! wp_style_is( 'lccb-banner', 'enqueued' ) ) {
			$settings = get_option( 'lccb_settings', array() );
			wp_enqueue_style( 'lccb-banner', LCCB_PLUGIN_URL . 'assets/css/lccb-banner.css', array(), LCCB_VERSION );
			wp_add_inline_style( 'lccb-banner', $this->build_css_vars( $settings ) );
		}

		$t       = $this->get_frontend_strings();
		$grouped = LCCB_Cookie_Registry::get_grouped();

		$categories = array(
			'necessary'   => $t['policy_necessary'],
			'statistics'  => $t['policy_statistics'],
			'marketing'   => $t['policy_marketing'],
			'preferences' => $t['policy_preferences'],
		);

		ob_start();
		?>
		<div class="lccb-policy-wrap">
		<?php
		foreach ( $categories as $cat_key => $cat_label ) :
			$cookies = $grouped[ $cat_key ] ?? array();
			?>
			<div class="lccb-policy-section">
				<h3 class="lccb-policy-cat-title"><?php echo esc_html( $cat_label ); ?></h3>
				<?php if ( empty( $cookies ) ) : ?>
					<p class="lccb-policy-empty">—</p>
				<?php else : ?>
				<table class="lccb-policy-table">
					<thead>
						<tr>
							<th><?php echo esc_html( $t['col_name'] ); ?></th>
							<th><?php echo esc_html( $t['col_vendor'] ); ?></th>
							<th><?php echo esc_html( $t['col_duration'] ); ?></th>
							<th><?php echo esc_html( $t['col_purpose'] ); ?></th>
						</tr>
					</thead>
					<tbody>
					<?php foreach ( $cookies as $ck ) : ?>
						<tr>
							<td><code class="lccb-policy-code"><?php echo esc_html( $ck['name'] ?? '' ); ?></code></td>
							<td><?php echo esc_html( $ck['vendor'] ?? '' ); ?></td>
							<td><?php echo esc_html( $ck['duration'] ?? '' ); ?></td>
							<td><?php echo esc_html( $ck['description'] ?? '' ); ?></td>
						</tr>
					<?php endforeach; ?>
					</tbody>
				</table>
				<?php endif; ?>
			</div>
		<?php endforeach; ?>
		</div>
		<?php
		return ob_get_clean();
	}

	/**
	 * AJAX handler: receives cookie names and third-party domains detected on the frontend.
	 */
	public function ajax_scan_cookies() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 );
		}
		check_ajax_referer( 'lccb_scan_cookies', 'nonce' );

		$raw_names = array();
		if ( isset( $_POST['cookies'] ) && is_array( $_POST['cookies'] ) ) {
			$raw_names = array_map( 'sanitize_text_field', wp_unslash( $_POST['cookies'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		}

		if ( ! empty( $raw_names ) ) {
			LCCB_Cookie_Scanner::scan_and_store( $raw_names );
		}

		// Save third-party domains detected via Performance API.
		$raw_domains = array();
		if ( isset( $_POST['domains'] ) && is_array( $_POST['domains'] ) ) {
			$raw_domains = array_map( 'sanitize_text_field', wp_unslash( $_POST['domains'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		}

		$raw_domains = array_filter(
			$raw_domains,
			function ( $d ) {
				return (bool) preg_match( '/^[a-z0-9]([a-z0-9\-\.]{0,253}[a-z0-9])?$/i', $d );
			}
		);

		if ( ! empty( $raw_domains ) ) {
			$existing = get_option( 'lccb_third_party_domains', array() );
			$merged   = array_unique( array_merge( $existing, array_values( $raw_domains ) ) );
			sort( $merged );
			update_option( 'lccb_third_party_domains', $merged );
			update_option( 'lccb_third_party_domains_ts', time() );
		}

		// Save third-party script URLs with optional cookie attributions (approach A).
		$script_items = array();
		if ( isset( $_POST['script_attribs'] ) && is_string( $_POST['script_attribs'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce already checked above
			$decoded = json_decode( sanitize_textarea_field( wp_unslash( $_POST['script_attribs'] ) ), true );
			if ( is_array( $decoded ) ) {
				foreach ( $decoded as $item ) {
					if ( empty( $item['url'] ) || ! is_string( $item['url'] ) ) {
						continue;
					}
					$cookies = array();
					if ( ! empty( $item['cookies'] ) && is_array( $item['cookies'] ) ) {
						$cookies = array_slice(
							array_filter(
								array_map( 'sanitize_text_field', $item['cookies'] ),
								function ( $c ) {
									return '' !== $c && strlen( $c ) <= 255;
								}
							),
							0,
							20
						);
					}
					$script_items[] = array(
						'url'     => sanitize_text_field( $item['url'] ),
						'cookies' => array_values( $cookies ),
					);
				}
			}
		}

		if ( ! empty( $script_items ) ) {
			LCCB_Cookie_Scanner::scan_and_store_scripts( $script_items );
		}

		wp_send_json_success();
	}

	/**
	 * AJAX handler: saves the visitor's consent choices and logs the record.
	 */
	public function save_consent() {
		check_ajax_referer( 'lccb_save_consent', 'nonce' );

		$action = isset( $_POST['action_type'] ) ? sanitize_key( $_POST['action_type'] ) : 'custom';

		$choices = array(
			'statistics'  => ! empty( $_POST['statistics'] ) ? 1 : 0,
			'marketing'   => ! empty( $_POST['marketing'] ) ? 1 : 0,
			'preferences' => ! empty( $_POST['preferences'] ) ? 1 : 0,
		);

		if ( 'accept_all' === $action ) {
			$choices = array(
				'statistics'  => 1,
				'marketing'   => 1,
				'preferences' => 1,
			);
		} elseif ( in_array( $action, array( 'reject_all', 'close' ), true ) ) {
			$choices = array(
				'statistics'  => 0,
				'marketing'   => 0,
				'preferences' => 0,
			);
		}

		$consent_id = LCCB_Consent_Manager::generate_consent_id();
		LCCB_Consent_Manager::log_consent( $consent_id, $choices, $action );

		wp_send_json_success(
			array(
				'consent_id' => $consent_id,
				'choices'    => $choices,
			)
		);
	}
}
