<?php
/**
 * @package BlaminhorEssentials
 * @var Blaminhor_Essentials_Module_Cookies               $module
 * @var Blaminhor_Essentials_Cookies_Service_Templates    $templates
 * @var array                                             $settings
 */

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

$bla_diag_rest_root = esc_url_raw( rest_url( 'blaminhor-essentials/cookies/v1/' ) );
$bla_diag_nonce     = wp_create_nonce( 'wp_rest' );
?>
<p>
	<?php esc_html_e( 'Compliance check: opens your homepage as an anonymous visitor (no cookies, no login), then lists every third-party script and iframe — telling you which ones are already blocked until consent, and which ones leak before the visitor agrees.', 'blaminhor-essentials' ); ?>
</p>

<p>
	<button type="button" class="button button-primary button-large" id="bla-diag-run">
		<?php esc_html_e( 'Scan my site', 'blaminhor-essentials' ); ?>
	</button>
	<span id="bla-diag-status" style="margin-left: 12px; color: #555;"></span>
</p>

<div id="bla-diag-empty" style="display:none;">
	<div class="notice notice-success inline">
		<p><?php esc_html_e( 'No third-party scripts or iframes detected on your homepage. Your site looks clean.', 'blaminhor-essentials' ); ?></p>
	</div>
</div>

<div id="bla-diag-results" style="display:none;">

	<!-- 🔴 Non-compliant section (shown first because it's actionable). -->
	<div id="bla-diag-noncompliant-wrap" style="display:none;">
		<h3 style="color:#c62828;">
			<?php esc_html_e( '⚠ Not compliant', 'blaminhor-essentials' ); ?>
			<span id="bla-diag-noncompliant-count" class="count"></span>
		</h3>
		<p class="description">
			<?php esc_html_e( 'These services load BEFORE the visitor consents. Click "Block until consent" to add them to your active list.', 'blaminhor-essentials' ); ?>
		</p>
		<table class="widefat striped" id="bla-diag-noncompliant"></table>
	</div>

	<!-- 🟢 Compliant section. -->
	<div id="bla-diag-compliant-wrap" style="display:none; margin-top: 30px;">
		<h3 style="color:#2e7d32;">
			<?php esc_html_e( 'OK Already blocked', 'blaminhor-essentials' ); ?>
			<span id="bla-diag-compliant-count" class="count"></span>
		</h3>
		<p class="description">
			<?php esc_html_e( 'These services are in your active list. They will only load after the visitor consents.', 'blaminhor-essentials' ); ?>
		</p>
		<table class="widefat striped" id="bla-diag-compliant"></table>
	</div>

	<!-- ⚪ Unknown section. -->
	<div id="bla-diag-unknown-wrap" style="display:none; margin-top: 30px;">
		<h3 style="color:#666;">
			<?php esc_html_e( '? Not in the catalog', 'blaminhor-essentials' ); ?>
			<span id="bla-diag-unknown-count" class="count"></span>
		</h3>
		<p class="description">
			<?php
			echo wp_kses_post(
				__( 'These third-party URLs aren\'t recognized as a known service. To block them, add a custom service template via the <code>blaminhor_cookies_service_templates</code> PHP filter.', 'blaminhor-essentials' )
			);
			?>
		</p>
		<table class="widefat striped" id="bla-diag-unknown"></table>
	</div>
</div>

<script>
(function () {
	'use strict';

	var REST_ROOT  = '<?php echo esc_js( $bla_diag_rest_root ); ?>';
	var REST_NONCE = '<?php echo esc_js( $bla_diag_nonce ); ?>';

	var STR = {
		stepCatalog: <?php echo wp_json_encode( __( 'Loading service catalog…', 'blaminhor-essentials' ) ); ?>,
		stepFetch:   <?php echo wp_json_encode( __( 'Fetching your homepage as an anonymous visitor…', 'blaminhor-essentials' ) ); ?>,
		stepMatch:   <?php echo wp_json_encode( __( 'Matching against the service catalog…', 'blaminhor-essentials' ) ); ?>,
		scanFailed:  <?php echo wp_json_encode( __( 'Scan failed:', 'blaminhor-essentials' ) ); ?>,
		colService:  <?php echo wp_json_encode( __( 'Service', 'blaminhor-essentials' ) ); ?>,
		colSource:   <?php echo wp_json_encode( __( 'Where', 'blaminhor-essentials' ) ); ?>,
		colCategory: <?php echo wp_json_encode( __( 'Category', 'blaminhor-essentials' ) ); ?>,
		colAction:   <?php echo wp_json_encode( __( 'Action', 'blaminhor-essentials' ) ); ?>,
		colUrl:      <?php echo wp_json_encode( __( 'URL', 'blaminhor-essentials' ) ); ?>,
		colType:     <?php echo wp_json_encode( __( 'Type', 'blaminhor-essentials' ) ); ?>,
		actBlock:    <?php echo wp_json_encode( __( 'Block until consent', 'blaminhor-essentials' ) ); ?>,
		actUnblock:  <?php echo wp_json_encode( __( 'Remove from active list', 'blaminhor-essentials' ) ); ?>,
		ctxThirdPartyScript: <?php echo wp_json_encode( __( 'Loaded from a third-party domain', 'blaminhor-essentials' ) ); ?>,
		ctxIframe:           <?php echo wp_json_encode( __( 'Embedded as an iframe', 'blaminhor-essentials' ) ); ?>,
		ctxInline:           <?php echo wp_json_encode( __( 'Inline script in the page source', 'blaminhor-essentials' ) ); ?>,
		working:     <?php echo wp_json_encode( __( 'Working…', 'blaminhor-essentials' ) ); ?>,
		failed:      <?php echo wp_json_encode( __( 'Failed', 'blaminhor-essentials' ) ); ?>
	};

	function $ ( sel ) { return document.querySelector( sel ); }

	function esc ( s ) {
		return String( s == null ? '' : s )
			.replace( /&/g, '&amp;' ).replace( /</g, '&lt;' )
			.replace( />/g, '&gt;' ).replace( /"/g, '&quot;' );
	}

	function row ( finding, mode ) {
		var actionHtml = '';
		if ( mode === 'noncompliant' ) {
			actionHtml = '<button type="button" class="button button-primary bla-diag-block" data-handle="' + esc( finding.handle ) + '" data-action="enable">' + esc( STR.actBlock ) + '</button>';
		} else if ( mode === 'compliant' ) {
			actionHtml = '<button type="button" class="button bla-diag-block" data-handle="' + esc( finding.handle ) + '" data-action="disable">' + esc( STR.actUnblock ) + '</button>';
		}

		return '<tr>' +
			'<td><strong>' + esc( finding.name || finding.handle || '—' ) + '</strong>' +
				( finding.provider ? '<br><span style="opacity:.7;font-size:12px;">' + esc( finding.provider ) + '</span>' : '' ) +
			'</td>' +
			'<td>' + esc( finding.context ) + ( finding.url ? '<br><code style="font-size:11px;">' + esc( finding.url.substring( 0, 80 ) ) + ( finding.url.length > 80 ? '…' : '' ) + '</code>' : '' ) + '</td>' +
			'<td>' + esc( finding.category || '—' ) + '</td>' +
			'<td>' + actionHtml + '</td>' +
		'</tr>';
	}

	function rowUnknown ( finding ) {
		return '<tr>' +
			'<td><code>' + esc( ( finding.url || '' ).substring( 0, 120 ) ) + '</code></td>' +
			'<td>' + esc( finding.type ) + '</td>' +
			'<td>' + esc( finding.context ) + '</td>' +
		'</tr>';
	}

	function fillTable ( id, data, mode ) {
		var html = '<thead><tr>' +
			'<th>' + esc( STR.colService ) + '</th>' +
			'<th>' + esc( STR.colSource ) + '</th>' +
			'<th>' + esc( STR.colCategory ) + '</th>' +
			'<th>' + esc( STR.colAction ) + '</th>' +
		'</tr></thead><tbody>';
		data.forEach( function ( f ) { html += row( f, mode ); } );
		html += '</tbody>';
		$( id ).innerHTML = html;
	}

	function fillUnknownTable ( id, data ) {
		var html = '<thead><tr>' +
			'<th>' + esc( STR.colUrl ) + '</th>' +
			'<th>' + esc( STR.colType ) + '</th>' +
			'<th>' + esc( STR.colSource ) + '</th>' +
		'</tr></thead><tbody>';
		data.forEach( function ( f ) { html += rowUnknown( f ); } );
		html += '</tbody>';
		$( id ).innerHTML = html;
	}

	function render ( res ) {
		var nc = res.nonCompliant || [], c = res.compliant || [], u = res.unknown || [];

		$( '#bla-diag-results' ).style.display = ( nc.length || c.length || u.length ) ? 'block' : 'none';
		$( '#bla-diag-empty' ).style.display   = ( nc.length || c.length || u.length ) ? 'none' : 'block';

		if ( nc.length ) {
			$( '#bla-diag-noncompliant-wrap' ).style.display = 'block';
			$( '#bla-diag-noncompliant-count' ).textContent = '(' + nc.length + ')';
			fillTable( '#bla-diag-noncompliant', nc, 'noncompliant' );
		} else {
			$( '#bla-diag-noncompliant-wrap' ).style.display = 'none';
		}

		if ( c.length ) {
			$( '#bla-diag-compliant-wrap' ).style.display = 'block';
			$( '#bla-diag-compliant-count' ).textContent = '(' + c.length + ')';
			fillTable( '#bla-diag-compliant', c, 'compliant' );
		} else {
			$( '#bla-diag-compliant-wrap' ).style.display = 'none';
		}

		if ( u.length ) {
			$( '#bla-diag-unknown-wrap' ).style.display = 'block';
			$( '#bla-diag-unknown-count' ).textContent = '(' + u.length + ')';
			fillUnknownTable( '#bla-diag-unknown', u );
		} else {
			$( '#bla-diag-unknown-wrap' ).style.display = 'none';
		}
	}

	// ------- Pattern matching helpers (mirror of the PHP Script_Blocker) ----

	function compilePattern ( raw ) {
		try {
			return new RegExp( raw, 'i' );
		} catch ( e ) {
			return null;
		}
	}

	function matchHandleForUrl ( catalog, url, patKey ) {
		for ( var i = 0; i < catalog.length; i++ ) {
			var svc = catalog[ i ];
			var patterns = svc[ patKey ] || [];
			for ( var j = 0; j < patterns.length; j++ ) {
				var rx = compilePattern( patterns[ j ] );
				if ( rx && rx.test( url ) ) {
					return svc.handle;
				}
			}
		}
		return null;
	}

	function matchHandleForText ( catalog, text ) {
		for ( var i = 0; i < catalog.length; i++ ) {
			var svc = catalog[ i ];
			var patterns = svc.scriptPatterns || [];
			for ( var j = 0; j < patterns.length; j++ ) {
				var rx = compilePattern( patterns[ j ] );
				if ( rx && rx.test( text ) ) {
					return svc.handle;
				}
			}
		}
		return null;
	}

	function isFirstPartyUrl ( url, firstPartyHost ) {
		if ( ! url ) { return true; }
		url = url.trim();
		if ( url.charAt( 0 ) === '#' || url.indexOf( 'data:' ) === 0 ) { return true; }
		// Relative URL (not protocol-relative).
		if ( url.charAt( 0 ) === '/' && url.charAt( 1 ) !== '/' ) { return true; }
		try {
			var u = new URL( url, 'https://' + firstPartyHost );
			return u.hostname.toLowerCase() === firstPartyHost.toLowerCase();
		} catch ( e ) {
			return true;
		}
	}

	function extractFindings ( html, catalog, firstPartyHost ) {
		var findings = [];
		var seen = {};

		var doc = new DOMParser().parseFromString( html, 'text/html' );

		// 1) <script src> tags.
		var scripts = doc.querySelectorAll( 'script[src]' );
		for ( var i = 0; i < scripts.length; i++ ) {
			var s = scripts[ i ];
			var type = ( s.getAttribute( 'type' ) || '' ).toLowerCase();
			if ( type === 'text/plain' ) { continue; }
			if ( s.hasAttribute( 'data-cookieconsent' ) ) { continue; }

			var src = s.getAttribute( 'src' );
			if ( isFirstPartyUrl( src, firstPartyHost ) ) { continue; }
			if ( seen[ src ] ) { continue; }
			seen[ src ] = true;

			findings.push( {
				type:    'script',
				url:     src,
				handle:  matchHandleForUrl( catalog, src, 'scriptPatterns' ),
				context: STR.ctxThirdPartyScript
			} );
		}

		// 2) <iframe src> tags.
		var iframes = doc.querySelectorAll( 'iframe[src]' );
		for ( var k = 0; k < iframes.length; k++ ) {
			var ifrSrc = iframes[ k ].getAttribute( 'src' );
			if ( isFirstPartyUrl( ifrSrc, firstPartyHost ) ) { continue; }
			if ( seen[ ifrSrc ] ) { continue; }
			seen[ ifrSrc ] = true;

			findings.push( {
				type:    'iframe',
				url:     ifrSrc,
				handle:  matchHandleForUrl( catalog, ifrSrc, 'iframePatterns' ),
				context: STR.ctxIframe
			} );
		}

		// 3) Inline scripts whose body matches a service pattern.
		var allScripts = doc.querySelectorAll( 'script:not([src])' );
		for ( var m = 0; m < allScripts.length; m++ ) {
			var inl = allScripts[ m ];
			var inlType = ( inl.getAttribute( 'type' ) || '' ).toLowerCase();
			if ( inlType === 'text/plain' ) { continue; }
			if ( inl.hasAttribute( 'data-cookieconsent' ) ) { continue; }
			if ( inl.id && inl.id.indexOf( 'blaminhor-' ) === 0 ) { continue; }

			var body = ( inl.textContent || '' ).trim();
			if ( ! body ) { continue; }

			var handle = matchHandleForText( catalog, body );
			if ( ! handle ) { continue; }
			var key = 'inline:' + handle;
			if ( seen[ key ] ) { continue; }
			seen[ key ] = true;

			findings.push( {
				type:    'inline-script',
				url:     '',
				handle:  handle,
				context: STR.ctxInline
			} );
		}

		return findings;
	}

	function classifyFindings ( findings, catalog, activeServices, allActiveImplicit ) {
		var byHandle = {};
		for ( var i = 0; i < catalog.length; i++ ) {
			byHandle[ catalog[ i ].handle ] = catalog[ i ];
		}
		var activeSet = {};
		activeServices.forEach( function ( h ) { activeSet[ h ] = true; } );

		var compliant = [], nonCompliant = [], unknown = [];
		findings.forEach( function ( f ) {
			if ( ! f.handle ) {
				unknown.push( f );
				return;
			}
			var tpl = byHandle[ f.handle ];
			if ( tpl ) {
				f.name     = tpl.name;
				f.provider = tpl.provider;
				f.category = tpl.category;
			}
			var isActive = allActiveImplicit || !! activeSet[ f.handle ];
			( isActive ? compliant : nonCompliant ).push( f );
		} );
		return { compliant: compliant, nonCompliant: nonCompliant, unknown: unknown };
	}

	// ------- Main flow ------------------------------------------------------

	function runScan () {
		var btn  = $( '#bla-diag-run' );
		var stat = $( '#bla-diag-status' );
		btn.disabled = true;
		stat.textContent = STR.stepCatalog;

		var ctx = null;

		fetch( REST_ROOT + 'diagnostic/scan', {
			method:  'GET',
			headers: { 'X-WP-Nonce': REST_NONCE },
			credentials: 'same-origin'
		} ).then( function ( r ) {
			if ( ! r.ok ) { return r.json().then( function ( j ) { throw new Error( j.message || ( 'HTTP ' + r.status ) ); } ); }
			return r.json();
		} ).then( function ( c ) {
			ctx = c;
			stat.textContent = STR.stepFetch;
			// Fetch the homepage as an anonymous visitor (omit cookies).
			return fetch( ctx.homepageUrl, {
				method:      'GET',
				credentials: 'omit',
				cache:       'no-store',
				headers:     { 'Accept': 'text/html' }
			} );
		} ).then( function ( r ) {
			if ( ! r.ok ) { throw new Error( 'Homepage returned HTTP ' + r.status ); }
			return r.text();
		} ).then( function ( html ) {
			stat.textContent = STR.stepMatch;
			var findings = extractFindings( html, ctx.templates, ctx.firstPartyHost );
			var classified = classifyFindings( findings, ctx.templates, ctx.activeServices, ctx.allActiveImplicit );
			stat.textContent = '';
			render( classified );
		} ).catch( function ( err ) {
			stat.textContent = STR.scanFailed + ' ' + ( err && err.message ? err.message : 'unknown' );
		} ).finally( function () {
			btn.disabled = false;
		} );
	}

	function toggleBlock ( ev ) {
		var btn = ev.target;
		if ( ! btn.classList.contains( 'bla-diag-block' ) ) { return; }
		var handle = btn.getAttribute( 'data-handle' );
		var action = btn.getAttribute( 'data-action' );
		btn.disabled = true;
		btn.textContent = STR.working;

		fetch( REST_ROOT + 'diagnostic/block', {
			method:  'POST',
			headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': REST_NONCE },
			credentials: 'same-origin',
			body: JSON.stringify( { handle: handle, action: action } )
		} ).then( function ( r ) {
			if ( ! r.ok ) { return r.json().then( function ( j ) { throw new Error( j.message || ( 'HTTP ' + r.status ) ); } ); }
			return r.json();
		} ).then( function () {
			runScan();
		} ).catch( function ( err ) {
			btn.disabled = false;
			btn.textContent = STR.failed + ': ' + ( err && err.message ? err.message : '' );
		} );
	}

	document.addEventListener( 'click', function ( ev ) {
		if ( ev.target.id === 'bla-diag-run' ) { runScan(); }
		else if ( ev.target.classList && ev.target.classList.contains( 'bla-diag-block' ) ) { toggleBlock( ev ); }
	} );
})();
</script>
