<?php

namespace ReadJutsu;

defined( 'ABSPATH' ) or die( 'Something went wrong.' );

class Frontend {

	private array $opts = [];
	private bool  $content_injected = false;
	private static bool $bar_injected = false;

	public function __construct() {
		$this->load_options();

		if ( ! get_option( 'readjutsu_activate' ) ) {
			return;
		}

		add_action( 'wp',              [ $this, 'init' ] );
		add_shortcode( 'readjutsu',     [ $this, 'shortcode' ] );
		add_shortcode( 'readjutsu_bar', [ $this, 'shortcode_bar' ] );
	}

	private function load_options() {
		// Pre-1.1 used a single shared post-type list.
		$legacy_types = (array) get_option( 'readjutsu_post_types', [ 'post' ] );

		// Pre-1.1 had no display mode; 'none' meant manual placement.
		$badge_display = get_option( 'readjutsu_badge_display', '' );
		$badge_pos_raw = get_option( 'readjutsu_badge_position', 'before' );
		if ( '' === $badge_display ) {
			$badge_display = ( 'none' === $badge_pos_raw ) ? 'manual' : 'auto';
		}
		$badge_position = ( 'after' === $badge_pos_raw ) ? 'after' : 'before';

		$progress_display = get_option( 'readjutsu_progress_display', 'auto' );
		if ( 'none' === $progress_display ) {
			$progress_display = 'manual';
		}

		// Premium styles fall back to free ones when the licence lapsed.
		$badge_style = get_option( 'readjutsu_badge_style', 'pill' );
		if ( in_array( $badge_style, [ 'floating', 'bordered', 'glass' ], true ) && ! readjutsu_can_use_premium() ) {
			$badge_style = 'pill';
		}

		$progress_style = get_option( 'readjutsu_progress_style', 'gradient' );
		if ( in_array( $progress_style, [ 'animated', 'percent', 'donut' ], true ) && ! readjutsu_can_use_premium() ) {
			$progress_style = 'gradient';
		}

		$this->opts = [
			'wpm'                => (int) get_option( 'readjutsu_wpm', 238 ),
			'include_images'     => (bool) get_option( 'readjutsu_include_images', false ),
			'badge_activate'     => (bool) get_option( 'readjutsu_badge_activate', true ),
			'badge_display'      => $badge_display,
			'badge_post_types'   => (array) get_option( 'readjutsu_badge_post_types', $legacy_types ),
			'badge_position'     => $badge_position,
			'badge_style'        => $badge_style,
			'badge_float_position' => get_option( 'readjutsu_badge_float_position', 'bottom-right' ),
			'badge_icon'         => (bool) get_option( 'readjutsu_badge_icon', true ),
			'badge_prefix'       => get_option( 'readjutsu_badge_prefix', __( 'Reading time:', 'readjutsu' ) ),
			'badge_postfix'      => get_option( 'readjutsu_badge_postfix', __( 'min read', 'readjutsu' ) ),
			'badge_color'        => get_option( 'readjutsu_badge_color', '#0069FF' ),
			'badge_text_color'   => get_option( 'readjutsu_badge_text_color', '#ffffff' ),
			'progress_activate'  => (bool) get_option( 'readjutsu_progress_activate', true ),
			'progress_display'   => $progress_display,
			'progress_post_types'=> (array) get_option( 'readjutsu_progress_post_types', $legacy_types ),
			'progress_scope'     => get_option( 'readjutsu_progress_scope', 'content' ),
			'progress_position'        => get_option( 'readjutsu_progress_position', 'top' ),
			'progress_donut_position'  => get_option( 'readjutsu_progress_donut_position', 'bottom-right' ),
			'progress_donut_size'      => (int) get_option( 'readjutsu_progress_donut_size', 46 ),
			'progress_style'     => $progress_style,
			'progress_color'     => get_option( 'readjutsu_progress_color', '#0069FF' ),
			'progress_color2'    => get_option( 'readjutsu_progress_color2', '#00AAFF' ),
			'progress_bg'        => get_option( 'readjutsu_progress_bg', '#ddeeff' ),
			'progress_height'    => (int) get_option( 'readjutsu_progress_height', 3 ),
		];
	}

	public function init() {
		// Base styles load whenever a feature is on, so manual block/shortcode
		// placements are styled anywhere.
		if ( $this->opts['badge_activate'] || $this->opts['progress_activate'] ) {
			add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
		}

		$premium = readjutsu_can_use_premium();

		if ( $premium && $this->opts['badge_activate'] ) {
			$this->load_badge_pro();
		}

		// The floating placement (Badge_Pro) replaces the inline injection.
		if ( ! $this->badge_floating_pro_active() && $this->badge_auto_active() ) {
			$priority = 'after' === $this->opts['badge_position'] ? 20 : 5;
			add_filter( 'the_content', [ $this, 'inject_badge_into_content' ], $priority );
		}

		if ( $premium && $this->opts['progress_activate'] ) {
			$this->load_bar_pro();
		}

		if ( $this->bar_auto_active() ) {
			$this->trigger_progress_bar_once();
		}
	}

	private function badge_auto_active(): bool {
		$active = $this->opts['badge_activate']
			&& 'auto' === $this->opts['badge_display']
			&& $this->is_allowed( $this->opts['badge_post_types'] );

		// Singular only: on archives get_queried_object_id() is a term id.
		if ( is_singular() ) {
			$active = (bool) apply_filters( 'readjutsu_badge_auto_active', $active, (int) get_queried_object_id() );
		}
		return $active;
	}

	private function bar_auto_active(): bool {
		$active = $this->opts['progress_activate']
			&& 'auto' === $this->opts['progress_display']
			&& $this->is_allowed( $this->opts['progress_post_types'] );

		if ( is_singular() ) {
			$active = (bool) apply_filters( 'readjutsu_bar_auto_active', $active, (int) get_queried_object_id() );
		}
		return $active;
	}

	private function badge_floating_pro_active(): bool {
		return readjutsu_can_use_premium()
			&& $this->opts['badge_activate']
			&& 'floating' === $this->opts['badge_style']
			&& $this->is_allowed( $this->opts['badge_post_types'] );
	}

	// Premium files are stripped from the free build, hence the file_exists guard.
	private function load_badge_pro(): void {
		$file = READJUTSU_PLUGIN_PATH . 'includes/class-readjutsu-badge-pro__premium_only.php';
		if ( file_exists( $file ) ) {
			require_once $file;
			new Badge_Pro( $this, $this->opts );
		}
	}

	private function load_bar_pro(): void {
		$file = READJUTSU_PLUGIN_PATH . 'includes/class-readjutsu-bar-pro__premium_only.php';
		if ( file_exists( $file ) ) {
			require_once $file;
			new Bar_Pro( $this->opts );
		}
	}

	public function enqueue_assets() {
		wp_enqueue_style(
			'readjutsu-public',
			READJUTSU_PLUGIN_URL . 'public/css/readjutsu-public.css',
			[],
			READJUTSU_VERSION
		);

		wp_add_inline_style( 'readjutsu-public', $this->build_inline_css() );

		if ( $this->bar_auto_active() ) {
			wp_enqueue_script(
				'readjutsu-public',
				READJUTSU_PLUGIN_URL . 'public/js/readjutsu-public.js',
				[],
				READJUTSU_VERSION,
				true
			);
			wp_localize_script( 'readjutsu-public', 'READJUTSU', [
				'scope'       => $this->opts['progress_scope'],
				'orientation' => in_array( $this->opts['progress_position'], [ 'left', 'right' ], true ) ? 'vertical' : 'horizontal',
			] );
		}
	}

	public function trigger_progress_bar_once(): void {
		if ( self::$bar_injected ) {
			return;
		}
		self::$bar_injected = true;

		// Shortcode/block in manual mode can fire after wp_enqueue_scripts.
		if ( did_action( 'wp_enqueue_scripts' ) ) {
			if ( ! wp_style_is( 'readjutsu-public', 'enqueued' ) ) {
				wp_enqueue_style( 'readjutsu-public', READJUTSU_PLUGIN_URL . 'public/css/readjutsu-public.css', [], READJUTSU_VERSION );
				wp_add_inline_style( 'readjutsu-public', $this->build_inline_css() );
			}
			if ( ! wp_script_is( 'readjutsu-public', 'enqueued' ) ) {
				wp_enqueue_script( 'readjutsu-public', READJUTSU_PLUGIN_URL . 'public/js/readjutsu-public.js', [], READJUTSU_VERSION, true );
				wp_localize_script( 'readjutsu-public', 'READJUTSU', [
					'scope'       => $this->opts['progress_scope'],
					'orientation' => in_array( $this->opts['progress_position'], [ 'left', 'right' ], true ) ? 'vertical' : 'horizontal',
				] );
			}
		}

		add_action( 'wp_footer', [ $this, 'inject_progress_bar' ] );

		// Marker lets the JS find the content start on classic themes.
		if ( ! ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) ) {
			add_filter( 'the_content', [ $this, 'inject_content_marker' ], 10 );
		}
	}

	public function build_inline_css(): string {
		return ':root{' . $this->build_inline_vars() . '}';
	}

	/**
	 * The CSS custom-property declarations only (no selector wrapper), so they
	 * can be applied either as a :root block or as a style="" attribute — the
	 * latter is how the block render styles the editor preview without echoing
	 * a <style> tag.
	 *
	 * @return string
	 */
	public function build_inline_vars(): string {
		$o = $this->opts;

		$is_vertical = in_array( $o['progress_position'], [ 'left', 'right' ], true );
		$grad_dir    = $is_vertical ? 'to top' : 'to right';
		$bar_bg      = 'gradient' === $o['progress_style']
			? 'linear-gradient(' . $grad_dir . ',' . esc_attr( $o['progress_color'] ) . ',' . esc_attr( $o['progress_color2'] ) . ')'
			: esc_attr( $o['progress_color'] );

		return sprintf(
			'--readjutsu-badge-bg:%s;--readjutsu-badge-color:%s;--readjutsu-progress-bar:%s;--readjutsu-progress-color:%s;--readjutsu-progress-bg:%s;--readjutsu-progress-height:%dpx;',
			esc_attr( $o['badge_color'] ),
			esc_attr( $o['badge_text_color'] ),
			$bar_bg,
			esc_attr( $o['progress_color'] ),
			esc_attr( $o['progress_bg'] ),
			$o['progress_height']
		);
	}

	public function inject_badge_into_content( string $content ): string {
		if ( ! in_the_loop() || ! is_singular() || $this->content_injected ) {
			return $content;
		}
		$this->content_injected = true;
		$badge = $this->render_badge( get_the_ID() );
		return 'after' === $this->opts['badge_position']
			? $content . $badge
			: $badge . $content;
	}

	public function render_badge( int $post_id ): string {
		// Single veto point for every render path (auto, shortcode, block, floating).
		if ( ! apply_filters( 'readjutsu_badge_render_allowed', true, $post_id ) ) {
			return '';
		}

		$time   = $this->calculate_reading_time( $post_id );
		$style  = esc_attr( $this->opts['badge_style'] );
		$prefix = esc_html( $this->opts['badge_prefix'] );
		$suffix = esc_html( $this->opts['badge_postfix'] );
		$icon   = $this->opts['badge_icon'] ? $this->clock_svg() : '';

		return sprintf(
			'<span class="readjutsu-badge readjutsu-badge--%s" aria-label="%s %s %s">%s%s<span class="readjutsu-badge__time">%s</span><span class="readjutsu-badge__unit">%s</span></span>',
			$style,
			esc_attr( $this->opts['badge_prefix'] ),
			esc_attr( $time ),
			esc_attr( $this->opts['badge_postfix'] ),
			$icon,
			$prefix ? '<span class="readjutsu-badge__label">' . $prefix . '</span>' : '',
			esc_html( $time ),
			$suffix
		);
	}

	public function inject_progress_bar() {
		if ( ! is_singular() ) {
			return;
		}

		if ( ! apply_filters( 'readjutsu_bar_render_allowed', true, (int) get_queried_object_id() ) ) {
			return;
		}
		$pos_map  = [ 'top' => 'readjutsu-progress--top', 'bottom' => 'readjutsu-progress--bottom', 'left' => 'readjutsu-progress--left', 'right' => 'readjutsu-progress--right' ];
		$position = $pos_map[ $this->opts['progress_position'] ] ?? 'readjutsu-progress--top';
		$html     = '<div id="readjutsu-progress" class="readjutsu-progress ' . esc_attr( $position ) . '" aria-hidden="true"><div id="readjutsu-progress-bar" class="readjutsu-progress__bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div></div>';

		// Bar_Pro hooks this filter for the premium styles (donut SVG, percent chip, animated bar).
		echo wp_kses( apply_filters( 'readjutsu_progress_bar_html', $html, $this->opts ), self::progress_bar_allowed_html() );
	}

	/**
	 * Allowed HTML for the progress-bar markup (base bar + premium donut/percent).
	 *
	 * @return array
	 */
	private static function progress_bar_allowed_html(): array {
		$global = [ 'id' => true, 'class' => true, 'style' => true, 'aria-hidden' => true ];
		return [
			'div'    => array_merge( $global, [ 'role' => true, 'aria-valuenow' => true, 'aria-valuemin' => true, 'aria-valuemax' => true ] ),
			'span'   => $global,
			'svg'    => array_merge( $global, [ 'width' => true, 'height' => true, 'viewbox' => true, 'xmlns' => true ] ),
			'circle' => array_merge( $global, [ 'cx' => true, 'cy' => true, 'r' => true, 'fill' => true, 'stroke-width' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-linecap' => true, 'transform' => true ] ),
			'text'   => array_merge( $global, [ 'x' => true, 'y' => true, 'text-anchor' => true ] ),
		];
	}

	public function inject_content_marker( string $content ): string {
		if ( ! in_the_loop() || ! is_singular() ) {
			return $content;
		}
		return '<div id="readjutsu-marker"></div>' . $content;
	}

	public function shortcode( array $atts = [] ): string {
		$atts    = shortcode_atts( [ 'id' => '' ], $atts, 'readjutsu' );
		$post_id = ! empty( $atts['id'] ) && is_numeric( $atts['id'] )
			? (int) $atts['id']
			: get_the_ID();

		return $this->render_badge( $post_id );
	}

	public function shortcode_bar( array $atts = [] ): string {
		if ( ! $this->opts['progress_activate'] ) {
			return '';
		}
		$this->trigger_progress_bar_once();
		return '';
	}

	public function calculate_reading_time( int $post_id ): string {
		$content = get_post_field( 'post_content', $post_id );
		$wpm     = max( 1, $this->opts['wpm'] );

		$image_count = substr_count( strtolower( $content ), '<img ' );
		$content     = strip_shortcodes( $content );
		$content     = wp_strip_all_tags( $content );
		$words       = count( preg_split( '/\s+/', trim( $content ), -1, PREG_SPLIT_NO_EMPTY ) );

		if ( $this->opts['include_images'] ) {
			$words += $this->image_time_in_words( $image_count, $wpm );
		}

		$minutes = (int) ceil( $words / $wpm );
		$minutes = (int) apply_filters( 'readjutsu_reading_time_minutes', $minutes, $post_id );

		return $minutes < 1 ? '< 1' : (string) $minutes;
	}

	private function image_time_in_words( int $count, int $wpm ): int {
		$extra = 0;
		for ( $i = 1; $i <= $count; $i++ ) {
			$seconds = $i >= 10 ? 3 : ( 12 - ( $i - 1 ) );
			$extra  += (int) round( $seconds * $wpm / 60 );
		}
		return $extra;
	}

	private function is_allowed( array $post_types ): bool {
		if ( empty( $post_types ) ) {
			return false;
		}
		return in_array( get_post_type(), $post_types, true );
	}

	private function clock_svg(): string {
		return '<svg class="readjutsu-badge__icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>';
	}
}
