<?php
/**
 * Per-post FAQ
 *
 * Adds a reorderable Question/Answer repeater metabox to blog posts, renders the
 * FAQ on single posts just below the author box, and outputs schema.org FAQPage
 * JSON-LD so the questions/answers validate as structured data.
 *
 * Data is stored in the post meta key `_bw_post_faq` as an array of
 * array( 'question' => string, 'answer' => string ) rows, in display order.
 */

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

define( 'BW_FAQ_META_KEY', '_bw_post_faq' );
define( 'BW_FAQ_HEADING_META_KEY', '_bw_post_faq_heading' );
define( 'BW_FAQ_ACCORDION_META_KEY', '_bw_post_faq_accordion' );
define( 'BW_FAQ_SETTINGS_OPTION', 'bw_post_faq_settings' );

/**
 * Built-in default FAQ heading. Filterable so it stays translatable.
 *
 * @return string
 */
function bw_post_faq_default_heading() {
	return __( 'Frequently Asked Questions', 'wpex' );
}

/**
 * Global FAQ settings (site-wide defaults), merged over the built-in defaults.
 *
 * @return array{heading:string,accordion:bool}
 */
function bw_post_faq_get_settings() {
	$option = get_option( BW_FAQ_SETTINGS_OPTION, array() );

	if ( ! is_array( $option ) ) {
		$option = array();
	}

	$heading = isset( $option['heading'] ) ? trim( (string) $option['heading'] ) : '';

	return array(
		'heading'   => '' !== $heading ? $heading : bw_post_faq_default_heading(),
		// Accordion defaults ON when no global value has been saved yet.
		'accordion' => isset( $option['accordion'] ) ? (bool) $option['accordion'] : true,
	);
}

/**
 * Resolve the heading for a post: per-post override, else global default.
 */
function bw_post_faq_resolve_heading( $post_id ) {
	$per_post = get_post_meta( $post_id, BW_FAQ_HEADING_META_KEY, true );

	if ( is_string( $per_post ) && '' !== trim( $per_post ) ) {
		return $per_post;
	}

	return bw_post_faq_get_settings()['heading'];
}

/**
 * Resolve the accordion mode for a post: per-post override ('on'/'off'), else
 * the global default.
 */
function bw_post_faq_resolve_accordion( $post_id ) {
	$per_post = get_post_meta( $post_id, BW_FAQ_ACCORDION_META_KEY, true );

	if ( 'on' === $per_post ) {
		return true;
	}

	if ( 'off' === $per_post ) {
		return false;
	}

	return bw_post_faq_get_settings()['accordion'];
}

/* -------------------------------------------------------------------------
 * Admin: global settings page (Settings > FAQ)
 * ---------------------------------------------------------------------- */

add_action( 'admin_menu', function() {
	add_options_page(
		__( 'FAQ Settings', 'wpex' ),
		__( 'FAQ', 'wpex' ),
		'manage_options',
		'bw-post-faq',
		'bw_post_faq_settings_page'
	);
} );

add_action( 'admin_init', function() {
	register_setting( 'bw_post_faq', BW_FAQ_SETTINGS_OPTION, array(
		'type'              => 'array',
		'sanitize_callback' => 'bw_post_faq_sanitize_settings',
		'default'           => array(),
	) );

	add_settings_section( 'bw_post_faq_main', '', '__return_false', 'bw-post-faq' );

	add_settings_field(
		'bw_post_faq_heading',
		__( 'Default FAQ heading', 'wpex' ),
		function() {
			$settings = bw_post_faq_get_settings();
			printf(
				'<input type="text" class="regular-text" name="%1$s[heading]" value="%2$s" placeholder="%3$s" />',
				esc_attr( BW_FAQ_SETTINGS_OPTION ),
				esc_attr( $settings['heading'] ),
				esc_attr( bw_post_faq_default_heading() )
			);
			echo '<p class="description">' . esc_html__( 'Shown above the questions on every post, unless overridden on an individual post.', 'wpex' ) . '</p>';
		},
		'bw-post-faq',
		'bw_post_faq_main'
	);

	add_settings_field(
		'bw_post_faq_accordion',
		__( 'Accordion style', 'wpex' ),
		function() {
			$settings = bw_post_faq_get_settings();
			printf(
				'<label><input type="checkbox" name="%1$s[accordion]" value="1" %2$s /> %3$s</label>',
				esc_attr( BW_FAQ_SETTINGS_OPTION ),
				checked( $settings['accordion'], true, false ),
				esc_html__( 'Show answers in an accordion (click a question to open it; one answer open at a time).', 'wpex' )
			);
			echo '<p class="description">' . esc_html__( 'When off, all answers are shown expanded. Can be overridden per post.', 'wpex' ) . '</p>';
		},
		'bw-post-faq',
		'bw_post_faq_main'
	);
} );

/**
 * Sanitize the global settings before save.
 */
function bw_post_faq_sanitize_settings( $input ) {
	$input = is_array( $input ) ? $input : array();

	return array(
		'heading'   => isset( $input['heading'] ) ? sanitize_text_field( $input['heading'] ) : '',
		'accordion' => ! empty( $input['accordion'] ) ? 1 : 0,
	);
}

function bw_post_faq_settings_page() {
	?>
	<div class="wrap">
		<h1><?php esc_html_e( 'FAQ Settings', 'wpex' ); ?></h1>
		<form method="post" action="options.php">
			<?php
			settings_fields( 'bw_post_faq' );
			do_settings_sections( 'bw-post-faq' );
			submit_button();
			?>
		</form>
	</div>
	<?php
}

/**
 * Return the sanitized, non-empty FAQ rows for a post, in saved order.
 *
 * @param int $post_id
 * @return array<int,array{question:string,answer:string}>
 */
function bw_get_post_faq( $post_id ) {
	$items = get_post_meta( $post_id, BW_FAQ_META_KEY, true );

	if ( ! is_array( $items ) ) {
		return array();
	}

	$out = array();

	foreach ( $items as $item ) {
		$question = isset( $item['question'] ) ? trim( $item['question'] ) : '';
		$answer   = isset( $item['answer'] ) ? trim( $item['answer'] ) : '';

		if ( '' === $question || '' === wp_strip_all_tags( $answer ) ) {
			continue;
		}

		$out[] = array(
			'question' => $question,
			'answer'   => $answer,
		);
	}

	return $out;
}

/* -------------------------------------------------------------------------
 * Admin: metabox
 * ---------------------------------------------------------------------- */

add_action( 'add_meta_boxes', function() {
	add_meta_box(
		'bw_post_faq',
		__( 'FAQ', 'wpex' ),
		'bw_post_faq_metabox',
		'post',
		'normal',
		'default'
	);
} );

/**
 * Output a single editor row. Used both for saved rows and the JS template.
 */
function bw_post_faq_render_row( $question = '', $answer = '' ) {
	?>
	<div class="bw-faq-row">
		<span class="bw-faq-handle dashicons dashicons-menu" title="<?php esc_attr_e( 'Drag to reorder', 'wpex' ); ?>"></span>
		<div class="bw-faq-fields">
			<input type="text" class="widefat bw-faq-q" name="bw_faq[question][]" value="<?php echo esc_attr( $question ); ?>" placeholder="<?php esc_attr_e( 'Question', 'wpex' ); ?>" />
			<textarea class="widefat bw-faq-a" name="bw_faq[answer][]" rows="4" placeholder="<?php esc_attr_e( 'Answer', 'wpex' ); ?>"><?php echo esc_textarea( $answer ); ?></textarea>
		</div>
		<button type="button" class="button-link bw-faq-remove" aria-label="<?php esc_attr_e( 'Remove question', 'wpex' ); ?>">&times;</button>
	</div>
	<?php
}

function bw_post_faq_metabox( $post ) {
	wp_nonce_field( 'bw_post_faq_save', 'bw_post_faq_nonce' );
	$items           = bw_get_post_faq( $post->ID );
	$global          = bw_post_faq_get_settings();
	$heading_meta    = get_post_meta( $post->ID, BW_FAQ_HEADING_META_KEY, true );
	$accordion_meta  = get_post_meta( $post->ID, BW_FAQ_ACCORDION_META_KEY, true );
	$accordion_label = $global['accordion'] ? __( 'On', 'wpex' ) : __( 'Off', 'wpex' );
	?>
	<div class="bw-faq-wrap">
		<p class="description">
			<?php esc_html_e( 'Add questions and answers shown below the post. Drag the handle to reorder. Basic HTML (links, bold) is allowed in answers. Leave empty to show nothing.', 'wpex' ); ?>
		</p>

		<table class="form-table bw-faq-options">
			<tr>
				<th scope="row"><label for="bw_faq_heading"><?php esc_html_e( 'Heading', 'wpex' ); ?></label></th>
				<td>
					<input type="text" id="bw_faq_heading" class="regular-text" name="bw_faq_heading" value="<?php echo esc_attr( is_string( $heading_meta ) ? $heading_meta : '' ); ?>" placeholder="<?php echo esc_attr( $global['heading'] ); ?>" />
					<p class="description"><?php esc_html_e( 'Leave blank to use the site default shown above.', 'wpex' ); ?></p>
				</td>
			</tr>
			<tr>
				<th scope="row"><label for="bw_faq_accordion"><?php esc_html_e( 'Accordion style', 'wpex' ); ?></label></th>
				<td>
					<select id="bw_faq_accordion" name="bw_faq_accordion">
						<option value="" <?php selected( ! in_array( $accordion_meta, array( 'on', 'off' ), true ) ); ?>><?php printf( esc_html__( 'Use site default (%s)', 'wpex' ), esc_html( $accordion_label ) ); ?></option>
						<option value="on" <?php selected( 'on', $accordion_meta ); ?>><?php esc_html_e( 'On', 'wpex' ); ?></option>
						<option value="off" <?php selected( 'off', $accordion_meta ); ?>><?php esc_html_e( 'Off', 'wpex' ); ?></option>
					</select>
				</td>
			</tr>
		</table>

		<div class="bw-faq-rows">
			<?php
			foreach ( $items as $item ) {
				bw_post_faq_render_row( $item['question'], $item['answer'] );
			}
			?>
		</div>
		<p>
			<button type="button" class="button bw-faq-add"><?php esc_html_e( '+ Add Question', 'wpex' ); ?></button>
		</p>
	</div>

	<script type="text/html" id="tmpl-bw-faq-row">
		<?php bw_post_faq_render_row(); ?>
	</script>

	<style>
		.bw-faq-row { display: flex; align-items: flex-start; gap: 8px; padding: 12px; margin-bottom: 8px; background: #fff; border: 1px solid #dcdcde; border-radius: 4px; }
		.bw-faq-handle { cursor: move; color: #8c8f94; margin-top: 4px; }
		.bw-faq-fields { flex: 1; }
		.bw-faq-fields .bw-faq-q { margin-bottom: 6px; font-weight: 600; }
		.bw-faq-remove { color: #b32d2e; font-size: 20px; line-height: 1; text-decoration: none; margin-top: 2px; }
		.bw-faq-remove:hover { color: #d63638; }
		.bw-faq-placeholder { border: 1px dashed #c3c4c7; background: #f0f0f1; border-radius: 4px; margin-bottom: 8px; }
	</style>
	<?php
}

add_action( 'admin_enqueue_scripts', function( $hook ) {
	if ( 'post.php' !== $hook && 'post-new.php' !== $hook ) {
		return;
	}

	$screen = get_current_screen();
	if ( ! $screen || 'post' !== $screen->post_type ) {
		return;
	}

	wp_enqueue_script( 'jquery-ui-sortable' );

	$js = <<<'JS'
jQuery(function($){
	var $wrap = $('.bw-faq-wrap');
	if ( ! $wrap.length ) { return; }
	var $rows = $wrap.find('.bw-faq-rows');

	$rows.sortable({
		handle: '.bw-faq-handle',
		placeholder: 'bw-faq-placeholder',
		forcePlaceholderSize: true,
		axis: 'y'
	});

	$wrap.on('click', '.bw-faq-add', function(){
		var tmpl = $('#tmpl-bw-faq-row').html();
		$rows.append(tmpl);
	});

	$wrap.on('click', '.bw-faq-remove', function(){
		$(this).closest('.bw-faq-row').remove();
	});
});
JS;

	wp_add_inline_script( 'jquery-ui-sortable', $js );
} );

/**
 * Persist the FAQ rows. Parallel question/answer arrays arrive in DOM order, so
 * dragging rows in the metabox is what determines the saved order.
 */
add_action( 'save_post_post', function( $post_id ) {
	if ( ! isset( $_POST['bw_post_faq_nonce'] ) || ! wp_verify_nonce( $_POST['bw_post_faq_nonce'], 'bw_post_faq_save' ) ) {
		return;
	}

	if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
		return;
	}

	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return;
	}

	$questions = array();
	$answers   = array();

	if ( isset( $_POST['bw_faq']['question'] ) && is_array( $_POST['bw_faq']['question'] ) ) {
		$questions = (array) $_POST['bw_faq']['question'];
	}

	if ( isset( $_POST['bw_faq']['answer'] ) && is_array( $_POST['bw_faq']['answer'] ) ) {
		$answers = (array) $_POST['bw_faq']['answer'];
	}

	$items = array();

	foreach ( $questions as $i => $question ) {
		$question = sanitize_text_field( wp_unslash( $question ) );
		$answer   = isset( $answers[ $i ] ) ? wp_kses_post( wp_unslash( $answers[ $i ] ) ) : '';

		if ( '' === trim( $question ) && '' === trim( wp_strip_all_tags( $answer ) ) ) {
			continue;
		}

		$items[] = array(
			'question' => $question,
			'answer'   => $answer,
		);
	}

	if ( $items ) {
		update_post_meta( $post_id, BW_FAQ_META_KEY, $items );
	} else {
		delete_post_meta( $post_id, BW_FAQ_META_KEY );
	}

	// Per-post heading override.
	$heading = isset( $_POST['bw_faq_heading'] ) ? sanitize_text_field( wp_unslash( $_POST['bw_faq_heading'] ) ) : '';

	if ( '' !== $heading ) {
		update_post_meta( $post_id, BW_FAQ_HEADING_META_KEY, $heading );
	} else {
		delete_post_meta( $post_id, BW_FAQ_HEADING_META_KEY );
	}

	// Per-post accordion override ('on' / 'off' / inherit).
	$accordion = isset( $_POST['bw_faq_accordion'] ) ? wp_unslash( $_POST['bw_faq_accordion'] ) : '';

	if ( in_array( $accordion, array( 'on', 'off' ), true ) ) {
		update_post_meta( $post_id, BW_FAQ_ACCORDION_META_KEY, $accordion );
	} else {
		delete_post_meta( $post_id, BW_FAQ_ACCORDION_META_KEY );
	}
} );

/* -------------------------------------------------------------------------
 * Front-end: render below the author box + FAQPage schema
 * ---------------------------------------------------------------------- */

/**
 * Insert the FAQ render callback immediately after the author box block in the
 * Total theme's single-post layout. Any block name that is a callable function
 * is invoked by the layout loop, so we register our function name as a block.
 */
add_filter( 'wpex_blog_single_layout_blocks', function( $blocks ) {
	if ( ! is_array( $blocks ) ) {
		return $blocks;
	}

	$rebuilt = array();

	foreach ( $blocks as $key => $value ) {
		$rebuilt[ $key ] = $value;

		if ( 'author_bio' === $key ) {
			$rebuilt['bw_render_post_faq'] = 'bw_render_post_faq';
		}
	}

	// Fallback: if there is no author box block, place the FAQ before related
	// posts, otherwise append it to the end.
	if ( ! isset( $rebuilt['bw_render_post_faq'] ) ) {
		$with_faq = array();

		foreach ( $rebuilt as $key => $value ) {
			if ( 'related_posts' === $key ) {
				$with_faq['bw_render_post_faq'] = 'bw_render_post_faq';
			}
			$with_faq[ $key ] = $value;
		}

		if ( ! isset( $with_faq['bw_render_post_faq'] ) ) {
			$with_faq['bw_render_post_faq'] = 'bw_render_post_faq';
		}

		$rebuilt = $with_faq;
	}

	return $rebuilt;
}, 20 );

/**
 * This site renders single posts through a WPBakery custom template
 * (`post_singular_template_content` theme mod) whose `[vcex_post_content]`
 * shortcode outputs the post body followed by the author box. That bypasses the
 * block layout above, so we also append the FAQ to that shortcode's output —
 * placing it directly after the author box, before the related-posts grid.
 */
add_filter( 'do_shortcode_tag', function( $output, $tag ) {
	if ( 'vcex_post_content' !== $tag ) {
		return $output;
	}

	return $output . bw_get_post_faq_html();
}, 10, 2 );

/**
 * Echo the FAQ markup. Used by the block-layout fallback.
 */
function bw_render_post_faq() {
	echo bw_get_post_faq_html();
}

/**
 * Build the FAQ section markup and its FAQPage JSON-LD. Returns an empty string
 * when not on a single post or when the post has no FAQ rows.
 *
 * @return string
 */
function bw_get_post_faq_html() {
	if ( ! is_singular( 'post' ) ) {
		return '';
	}

	$post_id = get_the_ID();
	$items   = bw_get_post_faq( $post_id );

	if ( empty( $items ) ) {
		return '';
	}

	$heading   = bw_post_faq_resolve_heading( $post_id );
	$accordion = bw_post_faq_resolve_accordion( $post_id );

	$schema = array(
		'@context'   => 'https://schema.org',
		'@type'      => 'FAQPage',
		'mainEntity' => array(),
	);

	$section_class = 'bw-post-faq' . ( $accordion ? ' bw-post-faq--accordion' : '' );

	$html  = '<section class="' . esc_attr( $section_class ) . '" aria-labelledby="bw-post-faq-heading">';
	$html .= bw_post_faq_styles();
	// Match the theme's "Related Posts" / share headings (vcex_heading, h3).
	$html .= '<h3 id="bw-post-faq-heading" class="vcex-module vcex-heading vcex-heading-plain bw-post-faq-heading" style="color:#494949;font-size:18px;"><span class="vcex-heading-inner clr">' . esc_html( $heading ) . '</span></h3>';
	$html .= '<div class="bw-faq-list">';

	$i = 0;

	foreach ( $items as $item ) {
		$answer_html = wpautop( wp_kses_post( $item['answer'] ) );

		$html .= '<div class="bw-faq-item">';

		if ( $accordion ) {
			$panel_id = 'bw-faq-a-' . $post_id . '-' . $i;
			$html    .= '<h4 class="bw-faq-q">';
			$html    .= '<button type="button" class="bw-faq-toggle" aria-expanded="false" aria-controls="' . esc_attr( $panel_id ) . '">';
			$html    .= '<span class="bw-faq-q-text">' . esc_html( $item['question'] ) . '</span>';
			$html    .= '<span class="bw-faq-icon" aria-hidden="true"></span>';
			$html    .= '</button></h4>';
			$html    .= '<div id="' . esc_attr( $panel_id ) . '" class="bw-faq-a" role="region" aria-labelledby="bw-post-faq-heading" hidden>' . $answer_html . '</div>';
		} else {
			$html .= '<h4 class="bw-faq-q">' . esc_html( $item['question'] ) . '</h4>';
			$html .= '<div class="bw-faq-a">' . $answer_html . '</div>';
		}

		$html .= '</div>';

		$schema['mainEntity'][] = array(
			'@type'          => 'Question',
			'name'           => wp_strip_all_tags( $item['question'] ),
			'acceptedAnswer' => array(
				'@type' => 'Answer',
				'text'  => trim( wp_kses_post( $item['answer'] ) ),
			),
		);

		$i++;
	}

	$html .= '</div>';
	$html .= '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>';

	if ( $accordion ) {
		$html .= bw_post_faq_accordion_script();
	}

	$html .= '</section>';

	return $html;
}

/**
 * Front-end FAQ styles (printed inline with the section).
 */
function bw_post_faq_styles() {
	return '<style>'
		. '.bw-post-faq{margin:40px 0;}'
		. '.bw-post-faq .bw-post-faq-heading{margin:0 0 20px;}'
		. '.bw-post-faq .bw-faq-item{margin:0 0 24px;padding:0 0 24px;border-bottom:1px solid #eaeaea;}'
		. '.bw-post-faq .bw-faq-item:last-child{border-bottom:0;margin-bottom:0;padding-bottom:0;}'
		. '.bw-post-faq .bw-faq-q{margin:0 0 8px;font-size:1.1em;}'
		. '.bw-post-faq .bw-faq-q,.bw-post-faq .bw-faq-q a,.bw-post-faq .bw-faq-toggle,.bw-post-faq .bw-faq-q-text{color:#000;}'
		. '.bw-post-faq .bw-faq-a>*:first-child{margin-top:0;}'
		. '.bw-post-faq .bw-faq-a>*:last-child{margin-bottom:0;}'
		// Accordion-specific.
		. '.bw-post-faq--accordion .bw-faq-q{margin:0;}'
		. '.bw-post-faq--accordion .bw-faq-toggle{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;padding:0;margin:0;background:none;border:0;cursor:pointer;text-align:left;font:inherit;color:inherit;}'
		. '.bw-post-faq--accordion .bw-faq-toggle:focus-visible{outline:2px solid currentColor;outline-offset:2px;}'
		. '.bw-post-faq--accordion .bw-faq-icon{flex:0 0 auto;position:relative;width:14px;height:14px;}'
		. '.bw-post-faq--accordion .bw-faq-icon::before,.bw-post-faq--accordion .bw-faq-icon::after{content:"";position:absolute;background:currentColor;transition:transform .2s ease;}'
		. '.bw-post-faq--accordion .bw-faq-icon::before{top:6px;left:0;width:14px;height:2px;}'
		. '.bw-post-faq--accordion .bw-faq-icon::after{top:0;left:6px;width:2px;height:14px;}'
		. '.bw-post-faq--accordion .bw-faq-toggle[aria-expanded="true"] .bw-faq-icon::after{transform:scaleY(0);}'
		. '.bw-post-faq--accordion .bw-faq-a{padding-top:12px;}'
		. '.bw-post-faq--accordion .bw-faq-a[hidden]{display:none;}'
		. '</style>';
}

/**
 * Accordion behaviour: open one answer at a time. Vanilla JS, no dependency.
 */
function bw_post_faq_accordion_script() {
	$js = <<<'JS'
(function(){
	var sections = document.querySelectorAll('.bw-post-faq--accordion');
	Array.prototype.forEach.call(sections, function(section){
		if (section.dataset.bwFaqReady) { return; }
		section.dataset.bwFaqReady = '1';
		var toggles = section.querySelectorAll('.bw-faq-toggle');
		Array.prototype.forEach.call(toggles, function(btn){
			btn.addEventListener('click', function(){
				var isOpen = btn.getAttribute('aria-expanded') === 'true';
				Array.prototype.forEach.call(toggles, function(other){
					other.setAttribute('aria-expanded', 'false');
					var p = document.getElementById(other.getAttribute('aria-controls'));
					if (p) { p.hidden = true; }
				});
				if (!isOpen) {
					btn.setAttribute('aria-expanded', 'true');
					var panel = document.getElementById(btn.getAttribute('aria-controls'));
					if (panel) { panel.hidden = false; }
				}
			});
		});
	});
})();
JS;

	return '<script>' . $js . '</script>';
}
