<?php
namespace IdeoFieldPorter;

defined( 'ABSPATH' ) || exit;

/**
 * Settings_Page
 *
 * Options:
 *   ideo_fp_overwrite         : bool
 *   ideo_fp_active_post_types : array
 *   ideo_fp_media_url_format  : 'relative' | 'absolute'
 *   ideo_fp_allowed_roles     : array of role slugs allowed to use the plugin
 *                               (besides Administrator, which always has access)
 */
class Settings_Page {

	const OPTION_POST_TYPES    = 'ideo_fp_active_post_types';
	const OPTION_MEDIA_URL     = 'ideo_fp_media_url_format'; // 'relative' | 'absolute'
	const OPTION_ALLOWED_ROLES = 'ideo_fp_allowed_roles';
	const CAP_ACCESS           = 'ideo_fp_access';
	const DEFAULT_COLOR        = '#0783be';

	// ── Hooks ─────────────────────────────────────────────────────────────────

	public function __construct() {
		add_action( 'admin_menu', array( $this, 'register_menu' ) );
		add_action( 'admin_init',       array( $this, 'register_settings' ) );
		add_action( 'admin_post_ideo_fp_reset_settings', array( $this, 'handle_reset_settings' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
		add_action( 'admin_notices', array( $this, 'render_settings_saved_toast' ), 1 );
		add_action( 'admin_notices', array( $this, 'suppress_wp_settings_errors' ), 0 );
		add_filter( 'pre_update_option', array( $this, 'suppress_wp_settings_notice' ), 10, 1 );
		add_action( 'admin_post_ideo_fp_clear_logs', array( $this, 'handle_clear_logs' ) );
		add_action( 'update_option_' . self::OPTION_ALLOWED_ROLES, array( __CLASS__, 'sync_access_capability' ), 10, 2 );
		add_action( 'add_option_' . self::OPTION_ALLOWED_ROLES, array( __CLASS__, 'sync_access_capability' ), 10, 0 );
		// WordPress core's options.php hardcodes a 'manage_options' check on
		// every settings-form submission, regardless of the page's own menu
		// capability — this filter is the only way to override it, otherwise
		// a role granted access via Settings → Access could see the page but
		// never actually save it.
		add_filter( 'option_page_capability_ideo_fp_settings_group', function() {
			return self::CAP_ACCESS;
		} );
	}

	// ── Menu ──────────────────────────────────────────────────────────────────

	public function register_menu() {
		add_options_page(
			__( 'ideo Field Porter', 'ideo-field-porter' ),
			__( 'ideo Field Porter', 'ideo-field-porter' ),
			self::CAP_ACCESS,
			'ideo-field-porter-settings',
			array( $this, 'render_page' )
		);
	}

	// ── Assets ───────────────────────────────────────────────────────────────

	public function enqueue_assets( $hook ) {
		if ( false === strpos( $hook, 'ideo-field-porter-settings' ) ) return;
		wp_enqueue_style( 'ideo-field-porter', IDEO_FP_URL . 'assets/admin.css', array(), IDEO_FP_VERSION );
		wp_enqueue_script( 'ideo-field-porter', IDEO_FP_URL . 'assets/admin.js', array( 'jquery' ), IDEO_FP_VERSION, true );
		$color = self::DEFAULT_COLOR;
		// 0.90, not the 0.25 used for the card border glow elsewhere on this
		// page — this is the same "mostly white" alternating-row tint as the
		// real live export table (see Admin_Page::render_page()), not a border
		// accent, so it needs the same blend amount to look pale, not saturated.
		$light = self::lighten_color( $color, 0.90 );
		$dynamic_css =
			'.ifp-preview th { background: ' . esc_attr( $color ) . ' !important; color: #fff !important; font-weight: 600; padding: 5px 8px; text-align: center; border: 1px solid #fff !important; }' . "\n" .
			'.ifp-preview tr:nth-child(even) td { background: ' . esc_attr( $light ) . '; }' . "\n" .
			':root { --ifp-preview-color: ' . esc_attr( $color ) . '; }';
		wp_add_inline_style( 'ideo-field-porter', $dynamic_css );

		// Convert all WP .notice elements to ifp-toast on the settings page.
		$toast_js = <<<'JS'
function ideoFpDismissToast(toast) {
	toast.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
	toast.style.opacity = '0';
	toast.style.transform = 'translateY(-8px)';
	setTimeout(function() { if (toast.parentElement) toast.remove(); }, 650);
}
document.addEventListener('DOMContentLoaded', function() {
	// Convert WP native settings-error notices to toasts.
	document.querySelectorAll('.settings-error').forEach(function(el) {
		var toast = document.createElement('div');
		var isError   = el.classList.contains('notice-error');
		var isWarning = el.classList.contains('notice-warning');
		toast.className = 'ifp-toast ' + (isError ? 'ifp-toast--error' : isWarning ? 'ifp-toast--warning' : 'ifp-toast--success');
		var p = el.querySelector('p');
		toast.innerHTML = (p ? p.outerHTML : el.innerHTML) + '<button type="button" class="ifp-toast__close" onclick="ideoFpDismissToast(this.parentElement)">&#x2715;</button>';
		document.body.appendChild(toast);
		el.remove();
		setTimeout(function() { ideoFpDismissToast(toast); }, 4000);
	});
	// Auto-dismiss PHP toasts already in the DOM.
	document.querySelectorAll('.ifp-toast').forEach(function(toast) {
		var closeBtn = toast.querySelector('.ifp-toast__close');
		if (closeBtn) closeBtn.setAttribute('onclick', 'ideoFpDismissToast(this.parentElement)');
		setTimeout(function() { ideoFpDismissToast(toast); }, 4000);
	});
});
JS;
		wp_add_inline_script( 'ideo-field-porter', $toast_js );
	}

	// ── Settings API ──────────────────────────────────────────────────────────

	public function register_settings() {
		register_setting( 'ideo_fp_settings_group', self::OPTION_POST_TYPES, array(
			'type'              => 'array',
			'default'           => array(),
			'sanitize_callback' => function( $val ) {
				return is_array( $val ) ? array_map( 'sanitize_key', $val ) : array();
			},
		) );
		register_setting( 'ideo_fp_settings_group', self::OPTION_MEDIA_URL, array(
			'type'              => 'string',
			'default'           => 'relative',
			'sanitize_callback' => function( $val ) {
				return in_array( $val, array( 'relative', 'absolute' ), true ) ? $val : 'relative';
			},
		) );
		register_setting( 'ideo_fp_settings_group', self::OPTION_ALLOWED_ROLES, array(
			'type'              => 'array',
			'default'           => array(),
			'sanitize_callback' => function( $val ) {
				// Real server-side gate — the checkboxes are only rendered as
				// `disabled` for non-Administrators (a UI nicety, not security),
				// so this is what actually stops a tampered request from
				// changing who has access. Non-admins get their submission
				// silently ignored: existing value returned unchanged.
				if ( ! current_user_can( 'manage_options' ) ) {
					return self::get_allowed_roles();
				}
				$roles = is_array( $val ) ? array_map( 'sanitize_key', $val ) : array();
				// Administrator always has access regardless of this setting —
				// no need to store it, and it can never be unchecked away.
				return array_values( array_diff( $roles, array( 'administrator' ) ) );
			},
		) );
	}

	// ── Render ────────────────────────────────────────────────────────────────

	public function render_page() {
		if ( ! current_user_can( self::CAP_ACCESS ) ) return;
		// Clear the auto-configured banner once the user has visited Settings.
		delete_option( 'ideo_fp_auto_configured' );

		// Show confirmation after reset.
		// Read-only UI flag from a redirect URL after an already nonce-verified
		// admin-post action (see handle_reset_settings()) — not a form being processed.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$show_reset_notice = isset( $_GET['ideo_fp_reset'] ) && '1' === sanitize_key( wp_unslash( $_GET['ideo_fp_reset'] ) );
		?>
		<div class="wrap ifp-wrap ifp-mode-standard">
		<div class="ifp-shell">

			<div class="ifp-shell__header">
				<div class="ifp-shell__brand">
					<span class="ifp-shell__brand-icon"><?php echo file_get_contents( plugin_dir_path( __FILE__ ) . 'images/field-porter-icon.svg' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Static SVG bundled with the plugin, not user input; inlined so it can inherit currentColor via CSS. ?></span>
					<div>
						<div class="ifp-shell__plugin-name"><?php esc_html_e( 'ideo Field Porter', 'ideo-field-porter' ); ?></div>
						<div class="ifp-shell__page-title"><?php esc_html_e( 'Settings', 'ideo-field-porter' ); ?></div>
					</div>
				</div>
				<div class="ifp-shell__mode">
					<?php if ( ideofp_fs()->is_registered() ) : ?>
					<a href="<?php echo esc_url( ideofp_fs()->get_account_url() ); ?>" id="ifp-account-indicator" class="button ifp-btn-danger"><?php esc_html_e( 'Account', 'ideo-field-porter' ); ?></a>
					<?php endif; ?>
					<a href="<?php echo esc_url( IDEO_FP_PRO_URL ); ?>" target="_blank" rel="noopener noreferrer" class="ifp-buy"><?php esc_html_e( 'PRO version', 'ideo-field-porter' ); ?></a>
				</div>
			</div>

			<div class="ifp-shell__body">

			<div class="ifp-settings-section">
				<h2 class="ifp-settings-section-title"><?php esc_html_e( 'Export / Import mode', 'ideo-field-porter' ); ?></h2>
				<p><?php esc_html_e( 'This version uses Standard mode for both export and import.', 'ideo-field-porter' ); ?></p>
				<ul class="ifp-settings-mode-desc">
					<li><?php echo wp_kses_post( __( '<strong>Standard</strong>: one row per post, single header row. Supports all field types. Layout fields are serialised into a single cell. Export to .xlsx, .csv or .xml. Import supports the same formats.', 'ideo-field-porter' ) ); ?></li>
					<li><?php echo wp_kses_post( __( '<strong>Hierarchical</strong>: supports nested layouts (Repeaters inside Repeaters, etc.). It produces a structured multi-level spreadsheet that preserves those relationships. (PRO version only)', 'ideo-field-porter' ) ); ?></li>
				</ul>

				<div class="ifp-mode-cards">
					<div class="ifp-mode-card">
						<div class="ifp-mode-card__header">
							<span class="ifp-mode-card__title"><?php esc_html_e( 'Standard', 'ideo-field-porter' ); ?></span>
							<span class="ifp-mode-card__badge ifp-mode-card__badge--active"><?php esc_html_e( 'Active', 'ideo-field-porter' ); ?></span>
						</div>
						<div class="ifp-preview">
							<table>
								<thead>
									<tr>
										<th><?php esc_html_e( 'Product', 'ideo-field-porter' ); ?></th>
										<th><?php esc_html_e( 'Category', 'ideo-field-porter' ); ?></th>
										<th><?php esc_html_e( 'Price', 'ideo-field-porter' ); ?></th>
										<th><?php esc_html_e( 'Dimensions', 'ideo-field-porter' ); ?></th>
									</tr>
								</thead>
								<tbody>
									<tr>
										<td>Oak Dining Table</td>
										<td>Furniture</td>
										<td>€ 649</td>
										<td>width : 180 cm<br>depth : 90 cm<br>height : 75 cm</td>
									</tr>
									<tr>
										<td>Linen Cushion Cover</td>
										<td>Textiles</td>
										<td>€ 28</td>
										<td>width : 50 cm<br>depth : 50 cm<br>height : 1 cm</td>
									</tr>
									<tr>
										<td>Matte Ceramic Vase</td>
										<td>Decoration</td>
										<td>€ 42</td>
										<td>width : 12 cm<br>depth : 12 cm<br>height : 28 cm</td>
									</tr>
								</tbody>
							</table>
						</div>
					</div>

					<div class="ifp-mode-card ifp-mode-card--locked">
						<div class="ifp-mode-card__header">
							<span class="ifp-mode-card__title"><?php esc_html_e( 'Hierarchical', 'ideo-field-porter' ); ?></span>
							<span class="ifp-mode-card__badge ifp-mode-card__badge--pro">🔒 PRO</span>
						</div>
						<div class="ifp-preview">
							<table>
								<thead>
									<tr>
										<th rowspan="2">Product</th>
										<th rowspan="2">Category</th>
										<th rowspan="2">Price</th>
										<th colspan="3"><?php esc_html_e( 'Dimensions', 'ideo-field-porter' ); ?></th>
									</tr>
									<tr>
										<th><?php esc_html_e( 'Width', 'ideo-field-porter' ); ?></th>
										<th><?php esc_html_e( 'Depth', 'ideo-field-porter' ); ?></th>
										<th><?php esc_html_e( 'Height', 'ideo-field-porter' ); ?></th>
									</tr>
								</thead>
								<tbody>
									<tr>
										<td>Oak Dining Table</td>
										<td>Furniture</td>
										<td>€ 649</td>
										<td>180 cm</td>
										<td>90 cm</td>
										<td>75 cm</td>
									</tr>
									<tr>
										<td>Linen Cushion Cover</td>
										<td>Textiles</td>
										<td>€ 28</td>
										<td>50 cm</td>
										<td>50 cm</td>
										<td>1 cm</td>
									</tr>
									<tr>
										<td>Matte Ceramic Vase</td>
										<td>Decoration</td>
										<td>€ 42</td>
										<td>12 cm</td>
										<td>12 cm</td>
										<td>28 cm</td>
									</tr>
								</tbody>
							</table>
						</div>
						<p class="ifp-mode-card__upsell">
							<?php echo wp_kses_post( sprintf(
								/* translators: %s: link to the PRO product page */
								__( 'Available in %s.', 'ideo-field-porter' ),
								'<a href="' . esc_url( IDEO_FP_PRO_URL ) . '" target="_blank" rel="noopener noreferrer">ideo Field Porter PRO</a>'
							) ); ?>
						</p>
					</div>
				</div>
			</div><!-- /ifp-settings-section -->

			<form method="post" action="options.php" id="ifp-settings-form">
				<?php settings_fields( 'ideo_fp_settings_group' ); ?>
				<?php // Force clean redirect URL — strip ideo_fp_reset and other transient params. ?>
				<input type="hidden" name="_wp_http_referer" value="<?php echo esc_attr( add_query_arg( array( 'page' => 'ideo-field-porter-settings' ), admin_url( 'options-general.php' ) ) ); ?>">

				<div class="ifp-settings-section">
					<h2 class="ifp-settings-section-title"><?php esc_html_e( 'Active post types', 'ideo-field-porter' ); ?></h2>
				<p><?php esc_html_e( 'Choose which post types display the ideo Field Porter menu. Post types with at least one ACF field group are enabled by default; others (including native Posts and Pages) are disabled until you turn them on here.', 'ideo-field-porter' ); ?></p>
				<?php
				$active_pts = self::get_active_post_types();
				$all_pts    = get_post_types( array( 'public' => true ), 'objects' );
				$default_pts = self::get_default_post_types();

				// Group by: builtin / custom (has ACF field groups) / other
				$groups = array(
					'builtin' => array( 'label' => __( 'WordPress built-in', 'ideo-field-porter' ), 'items' => array() ),
					'custom'  => array( 'label' => __( 'Custom post types', 'ideo-field-porter' ),   'items' => array() ),
					'other'   => array( 'label' => __( 'Other post types', 'ideo-field-porter' ),    'items' => array() ),
				);
				foreach ( $all_pts as $pt ) {
					if ( $pt->_builtin ) {
						$groups['builtin']['items'][] = $pt;
					} elseif ( self::has_acf_field_groups( $pt->name ) ) {
						$groups['custom']['items'][] = $pt;
					} else {
						$groups['other']['items'][] = $pt;
					}
				}
				foreach ( $groups as $group_key => $group ) :
					if ( empty( $group['items'] ) ) continue;
					?>
					<p class="ifp-pt-group-label"><?php echo esc_html( $group['label'] ); ?></p>
					<div class="ifp-pt-group-items">
					<?php foreach ( $group['items'] as $pt ) :
						$checked = in_array( $pt->name, $active_pts, true );
						$label   = $pt->labels->singular_name . ' <code class="ifp-pt-code">(' . $pt->name . ')</code>';
					?>
						<label class="ifp-pt-label">
							<input type="checkbox"
								name="<?php echo esc_attr( self::OPTION_POST_TYPES ); ?>[]"
								value="<?php echo esc_attr( $pt->name ); ?>"
								<?php checked( $checked ); ?>>
							<?php echo wp_kses_post( $label ); ?>
						</label>
					<?php endforeach; ?>
					</div>
				<?php endforeach; ?>

				</div><!-- /ifp-settings-section -->

				<div class="ifp-settings-section">
					<div class="ifp-settings-section-title-row">
						<h2 class="ifp-settings-section-title"><?php esc_html_e( 'Excel header colour', 'ideo-field-porter' ); ?></h2>
						<span class="ifp-mode-card__badge ifp-mode-card__badge--pro">🔒 PRO</span>
					</div>
					<p><?php esc_html_e( 'Choose the background colour for column headers in exported Excel files (.xlsx). A lighter tint is automatically applied to alternating data rows.', 'ideo-field-porter' ); ?></p>
					<div class="ifp-locked">
						<div class="ifp-picker-wrap">
							<div class="ifp-preview-swatches">
								<span class="ifp-preview-swatch" style="background:<?php echo esc_attr( self::DEFAULT_COLOR ); ?>;"></span>
								<span class="ifp-preview-label"><?php esc_html_e( 'Header', 'ideo-field-porter' ); ?></span>
								<span class="ifp-preview-swatch" style="background:<?php echo esc_attr( self::lighten_color( self::DEFAULT_COLOR, 0.90 ) ); ?>;"></span>
								<span class="ifp-preview-label"><?php esc_html_e( 'Alternating rows', 'ideo-field-porter' ); ?></span>
							</div>
						</div>
					</div>
					<p class="ifp-mode-card__upsell">
						<?php echo wp_kses_post( sprintf(
							/* translators: %s: link to the PRO product page */
							__( 'Available in the %s.', 'ideo-field-porter' ),
							'<a href="' . esc_url( IDEO_FP_PRO_URL ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'PRO version', 'ideo-field-porter' ) . '</a>'
						) ); ?>
					</p>
				</div><!-- /ifp-settings-section -->

				<div class="ifp-settings-section">
					<h2 class="ifp-settings-section-title"><?php esc_html_e( 'Media URL format', 'ideo-field-porter' ); ?></h2>
					<p><?php esc_html_e( 'Choose the URL format for media files (images, galleries, files) in exported spreadsheets.', 'ideo-field-porter' ); ?></p>
					<?php
					$current_media_fmt = self::get_media_url_format();
					$example_year_month = current_time( 'Y/m' );
					?>
					<fieldset class="ifp-media-fieldset">
						<label class="ifp-media-label">
							<input type="radio" name="<?php echo esc_attr( self::OPTION_MEDIA_URL ); ?>" value="relative" class="ifp-radio-mt" <?php checked( $current_media_fmt, 'relative' ); ?>>
							<span><strong><?php esc_html_e( 'Relative', 'ideo-field-porter' ); ?></strong> <code class="ifp-code-hint"><?php echo esc_html( $example_year_month ); ?>/image.jpg</code><br>
							<span class="ifp-settings-hint"><?php esc_html_e( 'Portable between environments — recommended for dev→prod migrations.', 'ideo-field-porter' ); ?></span></span>
						</label>
						<label class="ifp-media-label">
							<input type="radio" name="<?php echo esc_attr( self::OPTION_MEDIA_URL ); ?>" value="absolute" class="ifp-radio-mt" <?php checked( $current_media_fmt, 'absolute' ); ?>>
							<span><strong><?php esc_html_e( 'Absolute', 'ideo-field-porter' ); ?></strong> <code class="ifp-code-hint">https://site.com/wp-content/uploads/<?php echo esc_html( $example_year_month ); ?>/image.jpg</code><br>
							<span class="ifp-settings-hint"><?php esc_html_e( 'Full URL with domain — useful for sharing spreadsheets externally.', 'ideo-field-porter' ); ?></span></span>
						</label>
					</fieldset>
				</div><!-- /ifp-settings-section -->

				<div class="ifp-settings-section">
					<div class="ifp-settings-section-title-row">
						<h2 class="ifp-settings-section-title"><?php esc_html_e( 'Interface', 'ideo-field-porter' ); ?></h2>
						<span class="ifp-mode-card__badge ifp-mode-card__badge--pro">🔒 PRO</span>
					</div>
					<p><?php esc_html_e( 'Choose the color scheme for the ideo Field Porter interface.', 'ideo-field-porter' ); ?></p>
					<fieldset class="ifp-media-fieldset ifp-locked">
						<label class="ifp-media-label">
							<input type="radio" disabled class="ifp-radio-mt" checked>
							<span><strong><?php esc_html_e( 'Light', 'ideo-field-porter' ); ?></strong><br>
							<span class="ifp-settings-hint"><?php esc_html_e( 'Always use the light theme.', 'ideo-field-porter' ); ?></span></span>
						</label>
						<label class="ifp-media-label">
							<input type="radio" disabled class="ifp-radio-mt">
							<span><strong><?php esc_html_e( 'Dark', 'ideo-field-porter' ); ?></strong><br>
							<span class="ifp-settings-hint"><?php esc_html_e( 'Always use the dark theme.', 'ideo-field-porter' ); ?></span></span>
						</label>
						<label class="ifp-media-label">
							<input type="radio" disabled class="ifp-radio-mt">
							<span><strong><?php esc_html_e( 'Auto', 'ideo-field-porter' ); ?></strong><br>
							<span class="ifp-settings-hint"><?php esc_html_e( 'Follow the system preference.', 'ideo-field-porter' ); ?></span></span>
						</label>
					</fieldset>
					<p class="ifp-mode-card__upsell">
						<?php echo wp_kses_post( sprintf(
							/* translators: %s: link to the PRO product page */
							__( 'Available in the %s.', 'ideo-field-porter' ),
							'<a href="' . esc_url( IDEO_FP_PRO_URL ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'PRO version', 'ideo-field-porter' ) . '</a>'
						) ); ?>
					</p>
				</div><!-- /ifp-settings-section -->

				<?php if ( current_user_can( 'manage_options' ) ) : ?>
				<div class="ifp-settings-section">
					<h2 class="ifp-settings-section-title"><?php esc_html_e( 'Access', 'ideo-field-porter' ); ?></h2>
					<p><?php esc_html_e( 'Choose which roles (besides Administrator, which always has access) can use the ideo Field Porter Export/Import pages.', 'ideo-field-porter' ); ?></p>
					<?php
					$allowed_roles = self::get_allowed_roles();
					$builtin_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
					if ( ! function_exists( 'get_editable_roles' ) ) {
						require_once ABSPATH . 'wp-admin/includes/user.php';
					}
					$all_roles    = get_editable_roles();
					$role_groups  = array(
						'builtin' => array( 'label' => __( 'WordPress built-in', 'ideo-field-porter' ), 'items' => array() ),
						'custom'  => array( 'label' => __( 'Custom roles', 'ideo-field-porter' ),        'items' => array() ),
					);
					foreach ( $all_roles as $slug => $details ) {
						if ( 'administrator' === $slug ) continue; // always has access, shown separately below.
						$bucket = in_array( $slug, $builtin_roles, true ) ? 'builtin' : 'custom';
						$role_groups[ $bucket ]['items'][ $slug ] = $details;
					}
					?>
					<p class="ifp-pt-group-label"><?php esc_html_e( 'WordPress built-in', 'ideo-field-porter' ); ?></p>
					<div class="ifp-pt-group-items">
						<label class="ifp-pt-label" title="<?php echo esc_attr__( 'always has access', 'ideo-field-porter' ); ?>">
							<input type="checkbox" checked disabled>
							<?php esc_html_e( 'Administrator', 'ideo-field-porter' ); ?>
						</label>
					<?php foreach ( $role_groups['builtin']['items'] as $slug => $details ) :
						$checked = in_array( $slug, $allowed_roles, true );
					?>
						<label class="ifp-pt-label" title="(<?php echo esc_attr( $slug ); ?>)">
							<input type="checkbox"
								name="<?php echo esc_attr( self::OPTION_ALLOWED_ROLES ); ?>[]"
								value="<?php echo esc_attr( $slug ); ?>"
								<?php checked( $checked ); ?>>
							<?php echo esc_html( translate_user_role( $details['name'] ) ); ?>
						</label>
					<?php endforeach; ?>
					</div>
					<?php if ( ! empty( $role_groups['custom']['items'] ) ) : ?>
					<p class="ifp-pt-group-label"><?php esc_html_e( 'Custom roles', 'ideo-field-porter' ); ?></p>
					<div class="ifp-pt-group-items">
					<?php foreach ( $role_groups['custom']['items'] as $slug => $details ) :
						$checked = in_array( $slug, $allowed_roles, true );
					?>
						<label class="ifp-pt-label" title="(<?php echo esc_attr( $slug ); ?>)">
							<input type="checkbox"
								name="<?php echo esc_attr( self::OPTION_ALLOWED_ROLES ); ?>[]"
								value="<?php echo esc_attr( $slug ); ?>"
								<?php checked( $checked ); ?>>
							<?php echo esc_html( translate_user_role( $details['name'] ) ); ?>
						</label>
					<?php endforeach; ?>
					</div>
					<?php endif; ?>
				</div><!-- /ifp-settings-section -->
				<?php endif; ?>

			</form>

			<?php $this->render_logs_section(); ?>

			</div>

			<div class="ifp-shell__footer">
				<div>
				<?php if ( current_user_can( 'manage_options' ) ) : ?>
				<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="ifp-reset-form" onsubmit="return confirm('<?php echo esc_js( __( 'Reset all settings to defaults?', 'ideo-field-porter' ) ); ?>')">
					<?php wp_nonce_field( 'ideo_fp_reset_settings', 'ideo_fp_reset_nonce' ); ?>
					<input type="hidden" name="action" value="ideo_fp_reset_settings">
					<button type="submit" class="button ifp-btn-danger">
						<?php esc_html_e( 'Restore default settings', 'ideo-field-porter' ); ?>
					</button>
				</form>
				<?php endif; ?>
				</div>
				<button type="submit" form="ifp-settings-form" id="ifp-save-btn" class="button button-primary"><?php esc_html_e( 'Save Changes', 'ideo-field-porter' ); ?></button>
			</div>

		</div>
		</div>
		<?php
	}

	// ── Logs section ─────────────────────────────────────────────────────────

	private function render_logs_section() {
		$logs = \IdeoFieldPorter\Logger::get_all();
		// Read-only UI flag from a redirect URL after an already nonce-verified
		// admin-post action (see handle_clear_logs()) — not a form being processed.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$cleared = isset( $_GET['ideo_fp_logs_cleared'] ) && '1' === sanitize_key( wp_unslash( $_GET['ideo_fp_logs_cleared'] ) );
		$open    = $cleared;
		?>
		<div class="ifp-settings-section">
		<div class="ifp-logs-accordion<?php echo $open ? ' ifp-logs-accordion--open' : ''; ?>" id="ifp-logs-accordion">
		<button type="button" class="ifp-logs-toggle" id="ifp-logs-toggle" aria-expanded="<?php echo $open ? 'true' : 'false'; ?>" aria-controls="ifp-logs-body">
			<h2 class="ifp-settings-section-title">
				<?php esc_html_e( 'Import / Export logs', 'ideo-field-porter' ); ?>
				<span class="ifp-logs-count"><?php echo esc_html( count( $logs ) ); ?> / <?php echo esc_html( \IdeoFieldPorter\Logger::MAX_ENTRIES ); ?></span>
			</h2>
			<span class="ifp-logs-chevron">&#9660;</span>
		</button>

		<div class="ifp-logs-body" id="ifp-logs-body">


		<?php if ( empty( $logs ) ) : ?>
		<p class="ifp-logs-empty"><?php esc_html_e( 'No operations recorded yet.', 'ideo-field-porter' ); ?></p>
		<?php else : ?>
		<div class="ifp-logs-wrap">
			<table class="ifp-logs-table">
				<thead>
					<tr>
						<th><?php esc_html_e( 'Date', 'ideo-field-porter' ); ?></th>
						<th><?php esc_html_e( 'Type', 'ideo-field-porter' ); ?></th>
						<th><?php esc_html_e( 'Mode', 'ideo-field-porter' ); ?></th>
						<th><?php esc_html_e( 'Post type', 'ideo-field-porter' ); ?></th>
						<th><?php esc_html_e( 'File / Format', 'ideo-field-porter' ); ?></th>
						<th><?php esc_html_e( 'Status', 'ideo-field-porter' ); ?></th>
						<th><?php esc_html_e( 'Summary', 'ideo-field-porter' ); ?></th>
					</tr>
				</thead>
				<tbody>
				<?php foreach ( $logs as $entry ) :
					$summary = isset( $entry['summary'] ) ? $entry['summary'] : array();
					$parts   = array();
					if ( 'import' === $entry['type'] ) {
						if ( ! empty( $summary['created'] ) ) $parts[] = $summary['created'] . ' ' . esc_html__( 'created', 'ideo-field-porter' );
						if ( ! empty( $summary['updated'] ) ) $parts[] = $summary['updated'] . ' ' . esc_html__( 'updated', 'ideo-field-porter' );
						if ( ! empty( $summary['skipped'] ) ) $parts[] = $summary['skipped'] . ' ' . esc_html__( 'skipped', 'ideo-field-porter' );
						if ( ! empty( $summary['errors'] ) )  $parts[] = $summary['errors']  . ' ' . esc_html__( 'errors', 'ideo-field-porter' );
					}
					$summary_str = ! empty( $parts ) ? implode( ', ', $parts ) : '—';
					$file_or_fmt = 'import' === $entry['type']
						? ( ! empty( $entry['file'] ) ? basename( $entry['file'] ) : '—' )
						: ( ! empty( $entry['format'] ) ? $entry['format'] : '—' );
				?>
					<tr>
						<td class="ifp-logs-date"><?php echo esc_html( $entry['date'] ); ?></td>
						<td><span class="ifp-logs-badge ifp-logs-badge--<?php echo esc_attr( $entry['type'] ); ?>"><?php echo esc_html( ucfirst( $entry['type'] ) ); ?></span></td>
						<td><?php echo esc_html( ucfirst( $entry['mode'] ) ); ?></td>
						<td><code class="ifp-pt-code"><?php echo esc_html( $entry['post_type'] ); ?></code></td>
						<td><?php echo esc_html( $file_or_fmt ); ?></td>
						<td><span class="ifp-logs-status ifp-logs-status--<?php echo esc_attr( $entry['status'] ); ?>"><?php echo esc_html( ucfirst( $entry['status'] ) ); ?></span></td>
						<td class="ifp-logs-summary"><?php echo esc_html( $summary_str ); ?></td>
					</tr>
				<?php endforeach; ?>
				</tbody>
			</table>
		</div>
		<?php endif; ?>

		<?php if ( ! empty( $logs ) ) : ?>
		<div class="ifp-logs-actions">
			<button type="button" class="button ifp-locked" disabled title="<?php esc_attr_e( 'PRO only: download the log history as a CSV file.', 'ideo-field-porter' ); ?>">
				🔒 <?php esc_html_e( 'Export logs (CSV)', 'ideo-field-porter' ); ?>
			</button>
			<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="ifp-reset-form"
			      onsubmit="return confirm('<?php echo esc_js( __( 'Clear all logs? This cannot be undone.', 'ideo-field-porter' ) ); ?>')">
				<?php wp_nonce_field( 'ideo_fp_clear_logs', 'ideo_fp_clear_logs_nonce' ); ?>
				<input type="hidden" name="action" value="ideo_fp_clear_logs">
				<button type="submit" class="button ifp-btn-danger">
					<?php esc_html_e( 'Clear logs', 'ideo-field-porter' ); ?>
				</button>
			</form>
		</div>
		<?php endif; ?>

		</div><!-- /ifp-logs-body -->
		</div><!-- /ifp-logs-accordion -->

		</div><!-- /ifp-settings-section -->
		<?php
	}

	// ── Reset handler ────────────────────────────────────────────────────────

	public function handle_reset_settings() {
		if ( ! current_user_can( 'manage_options' ) ) wp_die( esc_html__( 'Access denied.', 'ideo-field-porter' ) );
		check_admin_referer( 'ideo_fp_reset_settings', 'ideo_fp_reset_nonce' );

		delete_option( 'ideo_fp_overwrite' ); // legacy cleanup — option removed, "Replace existing posts" is now a per-import choice
		delete_option( self::OPTION_POST_TYPES );
		delete_option( self::OPTION_MEDIA_URL );
		delete_option( self::OPTION_ALLOWED_ROLES );
		delete_option( 'ideo_fp_auto_configured' );
		self::sync_access_capability();

		wp_safe_redirect( add_query_arg(
			array( 'page' => 'ideo-field-porter-settings', 'ideo_fp_reset' => '1' ),
			admin_url( 'options-general.php' )
		) );
		exit;
	}

	// ── Settings saved toast ────────────────────────────────────────────────

	public function render_settings_saved_toast() {
		$screen = get_current_screen();
		if ( ! $screen || strpos( $screen->id, 'ideo-field-porter-settings' ) === false ) return;

		// Read-only UI flags from redirect URLs after already nonce-verified
		// admin-post/options.php actions — not form data being processed here.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$show_reset   = isset( $_GET['ideo_fp_reset'] ) && '1' === sanitize_key( wp_unslash( $_GET['ideo_fp_reset'] ) );
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$show_saved   = isset( $_GET['settings-updated'] ) && '1' === sanitize_key( wp_unslash( $_GET['settings-updated'] ) );
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$cleared      = isset( $_GET['ideo_fp_logs_cleared'] ) && '1' === sanitize_key( wp_unslash( $_GET['ideo_fp_logs_cleared'] ) );

		if ( $show_reset ) {
			echo '<div class="ifp-toast ifp-toast--success"><p>' . esc_html__( 'Settings restored to defaults.', 'ideo-field-porter' ) . '</p><button type="button" class="ifp-toast__close" onclick="this.parentElement.remove()">&#x2715;</button></div>';
		}
		if ( $show_saved ) {
			echo '<div class="ifp-toast ifp-toast--success"><p>' . esc_html__( 'Settings saved.', 'ideo-field-porter' ) . '</p><button type="button" class="ifp-toast__close" onclick="this.parentElement.remove()">&#x2715;</button></div>';
		}
		if ( $cleared ) {
			echo '<div class="ifp-toast ifp-toast--success"><p>' . esc_html__( 'Logs cleared.', 'ideo-field-porter' ) . '</p><button type="button" class="ifp-toast__close" onclick="this.parentElement.remove()">&#x2715;</button></div>';
		}
	}

	public function suppress_wp_settings_notice( $value ) { return $value; }

	public function suppress_wp_settings_errors() {
		$screen = get_current_screen();
		if ( ! $screen || strpos( $screen->id, 'ideo-field-porter-settings' ) === false ) return;
		add_filter( 'wp_redirect', function( $location ) {
			return $location;
		} );
		// Remove WP's auto-added settings errors notice.
		global $wp_settings_errors;
		$wp_settings_errors = array();
	}

	// ── Clear logs handler ───────────────────────────────────────────────────

	public function handle_clear_logs() {
		if ( ! current_user_can( self::CAP_ACCESS ) ) wp_die( esc_html__( 'Access denied.', 'ideo-field-porter' ) );
		check_admin_referer( 'ideo_fp_clear_logs', 'ideo_fp_clear_logs_nonce' );
		\IdeoFieldPorter\Logger::clear();
		wp_safe_redirect( add_query_arg(
			array( 'page' => 'ideo-field-porter-settings', 'ideo_fp_logs_cleared' => '1' ),
			admin_url( 'options-general.php' )
		) );
		exit;
	}

	public static function get_mode() {
		// Standard mode only — Hierarchical mode is a Pro feature.
		return 'standard';
	}

	// ── Static helpers ────────────────────────────────────────────────────────

	public static function get_media_url_format() {
		$val = get_option( self::OPTION_MEDIA_URL, 'relative' );
		return in_array( $val, array( 'relative', 'absolute' ), true ) ? $val : 'relative';
	}

	/**
	 * Roles (besides Administrator, which always has access) allowed to use
	 * the plugin's Export/Import pages.
	 *
	 * @return string[] Role slugs.
	 */
	public static function get_allowed_roles() {
		$roles = get_option( self::OPTION_ALLOWED_ROLES, array() );
		return is_array( $roles ) ? $roles : array();
	}

	/**
	 * Sync the `ideo_fp_access` capability across every role on the site so
	 * it matches the "Allowed roles" setting: added to roles in the list
	 * (Administrator always included), removed from every other role.
	 *
	 * WordPress checks page access by capability, not by role directly — this
	 * is the standard way to let a settings-driven list of roles control
	 * access to an admin page via add_submenu_page().
	 */
	public static function sync_access_capability() {
		if ( ! function_exists( 'get_editable_roles' ) ) {
			require_once ABSPATH . 'wp-admin/includes/user.php';
		}
		$allowed = array_merge( array( 'administrator' ), self::get_allowed_roles() );
		foreach ( get_editable_roles() as $slug => $details ) {
			$role = get_role( $slug );
			if ( ! $role ) continue;
			if ( in_array( $slug, $allowed, true ) ) {
				if ( ! $role->has_cap( self::CAP_ACCESS ) ) $role->add_cap( self::CAP_ACCESS );
			} else {
				if ( $role->has_cap( self::CAP_ACCESS ) ) $role->remove_cap( self::CAP_ACCESS );
			}
		}
	}

	public static function get_active_post_types() {
		$saved = get_option( self::OPTION_POST_TYPES, null );
		if ( null !== $saved && is_array( $saved ) && ! empty( $saved ) ) return $saved;
		return self::get_default_post_types();
	}

	public static function get_default_post_types() {
		// A post type is considered active by default only if it is a custom
		// (non-builtin) post type with at least one ACF field group registered.
		// Native WordPress post types (Posts, Pages…) are never activated by
		// default, even if they have ACF fields — the user must opt them in.
		$all    = get_post_types( array( 'public' => true ), 'objects' );
		$result = array();
		foreach ( $all as $pt ) {
			if ( $pt->_builtin ) continue;
			// Activate only if it has ACF field groups.
			if ( self::has_acf_field_groups( $pt->name ) ) {
				$result[] = $pt->name;
			}
		}
		return $result;
	}

	/**
	 * Returns true if the given post type has at least one ACF field group.
	 */
	public static function has_acf_field_groups( $post_type ) {
		if ( ! function_exists( 'acf_get_field_groups' ) ) return false;
		$groups = acf_get_field_groups( array( 'post_type' => $post_type ) );
		return ! empty( $groups );
	}

	/**
	 * Mix a hex color with white for alternating rows / tints.
	 * amount = 0 → original colour, amount = 1 → white.
	 */
	public static function lighten_color( $hex, $amount = 0.90 ) {
		$hex = ltrim( $hex, '#' );
		$r   = hexdec( substr( $hex, 0, 2 ) );
		$g   = hexdec( substr( $hex, 2, 2 ) );
		$b   = hexdec( substr( $hex, 4, 2 ) );
		$r   = (int) round( $r + ( 255 - $r ) * $amount );
		$g   = (int) round( $g + ( 255 - $g ) * $amount );
		$b   = (int) round( $b + ( 255 - $b ) * $amount );
		return sprintf( '#%02x%02x%02x', $r, $g, $b );
	}

	/**
	 * Generate a darker shade of a hex colour — used for the "actively
	 * scrolling" scrollbar state, one step darker than the plain hover tint.
	 * Mixes the colour with black at the given amount.
	 */
	public static function darken_color( $hex, $amount = 0.25 ) {
		$hex = ltrim( $hex, '#' );
		$r   = hexdec( substr( $hex, 0, 2 ) );
		$g   = hexdec( substr( $hex, 2, 2 ) );
		$b   = hexdec( substr( $hex, 4, 2 ) );
		$r   = (int) round( $r * ( 1 - $amount ) );
		$g   = (int) round( $g * ( 1 - $amount ) );
		$b   = (int) round( $b * ( 1 - $amount ) );
		return sprintf( '#%02x%02x%02x', $r, $g, $b );
	}

	/**
	 * Convert a #rrggbb colour to the FF-prefixed ARGB format used in XLSX styles.
	 */
	public static function hex_to_xlsx( $hex ) {
		return 'FF' . strtoupper( ltrim( $hex, '#' ) );
	}
}
